Integrate Voice Search into Google Now, googlenow
Original article title: Use Voice Search to integrate with Google Now
Link: http://antonioleiva.com/voice_search_google_now/
Antonio Leiva (http://antonioleiva.com/about)
Original article published:
One of the best Android capabilities is to integrate our apps into its ecosystem in different ways. Apps can communicate with each other, which gives us great flexibility to create a unique application experience.
Integrating Google applications is a typical example. There are many different features that can help us increase the APP's popularity, such as APP indexing or a group of powerful voice functions.
Voice Search
Although the process of implementing Voice Search in the APP is similar to that of any other voice functions, in this article, I will focus on how to implement the Voice Search function in the APP. You can also try this example in Play Music:
- Good,GoogleInPlay Music, SearchBeatles
This command will enable the search for Beatles in the Play Music APP.
How to Implement Voice Search
When the Voice Search is started, our APP receives an Intent for querying text, which must be captured and analyzed. Therefore, the first part is to specify which Activity receives this Intent:
1 <activity android:name=".MainActivity" android:launchMode="singleTask" >2 <intent-filter>3 <action android:name="com.google.android.gms.actions.SEARCH_ACTION"/>4 <category android:name="android.intent.category.DEFAULT"/>5 </intent-filter>6 </activity>
This action is called com. google. android. gms. actions. SEARCH_ACTION. Therefore, MainActivity is used to process such Intent. In addition, I use singleTask startup mode, so that the MainActivity is created only once. Otherwise, a new Activity instance is created every time you receive the Intent.
The next step is to process it in MainActivity. When we use singleTask mode, Intent can be received in two different locations of the Activity: first, the Activity is created using getIntent (), and then the onNewIntent method. In this way, you need to create a processing method and call it when you need it:
1 private static final String ACTION_VOICE_SEARCH = "com.google.android.gms.actions.SEARCH_ACTION";2 ...3 private void handleVoiceSearch(Intent intent) {4 if (intent != null && ACTION_VOICE_SEARCH.equals(intent.getAction())) {5 String query = intent.getStringExtra(SearchManager.QUERY);6 setSearchViewVisible(true);7 searchView.setQuery(query, true);8 }9 }
This method checks whether Intent is null or whether it is an action to detect before receiving the query text. It is an extra action inside Intent. The additional action keyword to QUERY is SearchManager. QUERY.
After searchView, set the query and submit it for execution. The method is onNewIntent:
1 @Override protected void onNewIntent(Intent intent) {2 super.onNewIntent(intent);3 handleVoiceSearch(intent);4 }
The UI is also ready (in our example, when the menu pops up, we access SearchView). You will see it later.
In my example, the UI is based on the SearchView inside the Toolbar. You can see how to implement SearchView in the previous article, but I will explain how to implement it. First, the menu action is generated ):
1 <menu xmlns:android="http://schemas.android.com/apk/res/android"2 xmlns:app="http://schemas.android.com/apk/res-auto">3 <item4 android:id="@+id/action_search"5 android:title="@string/action_search"6 android:icon="@drawable/ic_search"7 app:actionViewClass="android.support.v7.widget.SearchView"8 app:showAsAction="ifRoom" />9 </menu>
Then, when the menu pops up, you request SearchView:
1 @Override public boolean onCreateOptionsMenu(Menu menu) { 2 getMenuInflater().inflate(R.menu.main, menu); 3 4 MenuItem searchItem = menu.findItem(R.id.action_search); 5 searchView = (SearchView) MenuItemCompat.getActionView(searchItem); 6 7 searchView.setOnSearchClickListener(new View.OnClickListener() { 8 @Override public void onClick(View v) { 9 setSearchViewVisible(true);10 }11 });12 13 searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {14 @Override public boolean onQueryTextSubmit(String query) {15 Toast.makeText(MainActivity.this, query, Toast.LENGTH_LONG).show();16 searchView.clearFocus();17 return true;18 }19 20 @Override public boolean onQueryTextChange(String newText) {21 return false;22 }23 });24 25 handleVoiceSearch(getIntent());26 27 return true;28 }29 30 private void setSearchViewVisible(boolean visible) {31 32 if (searchView.isIconified() == visible) {33 searchView.setIconified(!visible);34 }35 36 if (getSupportActionBar() != null) {37 getSupportActionBar().setDisplayHomeAsUpEnabled(visible);38 }39 }
How to try
Because the APP needs to be published to the Play store before Google Now can detect the APP, we cannot try this example directly from Google Now. However, we can use ADB to debug it. This command is:
- Adb shell am start-a com. google. android. gms. actions. SEARCH_ACTION-e querySearchquery App_package
For the example downloaded from my code library, you can do this:
- Adb shell am start-a com. google. android. gms. actions. SEARCH_ACTION-e query VoiceSearch com. antonioleiva. googlenowsearch
When the APP is completely closed and started, you can try this example. In this way, two possible ways can be tested.
Additional instructions: Implementation of the Kotlin Language
You may know that, because I think the Kotlin language can replace the Java language very well, Kotlin can make our code more concise and readable, so we have discussed many features of the Kotlin language these days. As an example, I will simplify onCreateOptionsMenu and you can find the complete code in the same code library.
The ability to implement extended functions can help us reduce lengthy code. For example, you can create an extension function for Menu, locate the ActionView based on the action id, and return the hierarchical view:
1 inline fun <reified T : View?> Menu.findCompatActionView(actionRes: Int): T {2 val searchItem = findItem(actionRes)3 return MenuItemCompat.getActionView(searchItem) as T4 }
Now you can do this:
1 searchView = menu.findCompatActionView(R.id.action_search)
Another extension function helps us to write OueryTextListener in a clear way:
1 fun SearchView.onQueryText(submit: (String) -> Boolean = { false }, textChange: (String) -> Boolean = { false }) { 2 3 this.setOnQueryTextListener(object : SearchView.OnQueryTextListener { 4 5 override fun onQueryTextSubmit(query: String): Boolean = submit(query) 6 7 override fun onQueryTextChange(newText: String): Boolean = textChange(newText) 8 9 })10 }
This function receives a pair of functions, one for each method in the listener, and gives their default values. In this way, we only need to define what we need. If we only need the first function (we use the default function for the second function), now I can do this:
1 searchView.onQueryText ({2 longToast(it)3 searchView.clearFocus()4 true5 })
In the end, this function is like this:
1 override fun onCreateOptionsMenu(menu: Menu): Boolean { 2 menuInflater.inflate(R.menu.main, menu) 3 4 searchView = menu.findCompatActionView(R.id.action_search) 5 searchView.setOnSearchClickListener { setSearchViewVisible(true) } 6 7 searchView.onQueryText ({ 8 longToast(it) 9 searchView.clearFocus()10 true11 })12 13 intent?.let { handleVoiceSearch(it) }14 15 return true16 }
As you can see, if Intent can be null at this position (for example, in a regular activity creation), you must check whether Intent is null before using it. Using the let function can avoid the creation of the if condition. When the object is called, it can only enter the object.
If you are interested in Kotlin, you can search for Kotlin articles or purchase my book "Kotlin for Android Developers.