Many apps work with collections of data, and users search through that data in order to find what they are looking for. Android has several search mechanisms that are integrated with the OS.
Integrating search into our app is very easy, only requiring a search activity and a few attributes on the activities:
public class SearchActivity : Activity { protected override void OnCreate(Bundle bundle) { ... HandleIntent(Intent); } protected override void OnNewIntent(Intent intent) { HandleIntent(intent); } private void HandleIntent(Intent intent) { if (intent.Action == Intent.ActionSearch) { string query = intent.GetStringExtra(SearchManager.Query); } } }
[Register]
attribute:[Register("com.xamarincookbook.SearchActivity")]
[IntentFilter(new[] { Intent.ActionSearch })]
xml
folder under Resources
, that contains various properties for the search metadata.For example, the contents of the searchable.xml
file can be as follows:
<?xml version="1.0" encoding="utf-8"?> <searchable xmlns:android="http://schemas.android.com/apk/res/android" android:label="Xamarin Cookbook" android:hint="Search Xamarin Cookbook for code" android:voiceSearchMode= "showVoiceSearchButton|launchRecognizer"> </searchable>
android.app.searchable
:[MetaData( "android.app.searchable", Resource = "@xml/searchable")]
Our search activity is finished, so we can start building the activities that are searchable or expose some search functionality:
[MetaData( "android.app.default_searchable", Value = "com.xamarincookbook.SearchActivity")]
[assembly: MetaData( "android.app.default_searchable", Value = "com.xamarincookbook.SearchActivity")]
<item xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/searchItem" android:title="Search" android:icon="@android:drawable/ic_menu_search" app:actionViewClass="android.support.v7.widget.SearchView" app:showAsAction="always" />
ActionView
to the support library search view:public override bool OnCreateOptionsMenu(IMenu menu) { MenuInflater.Inflate(Resource.Menu.ActionBar, menu); var searchItem = menu.FindItem(Resource.Id.searchItem); var view = searchItem.ActionView.JavaCast<SearchView>(); var manager = SearchManager.FromContext(this); var info = manager.GetSearchableInfo(ComponentName); view.SetSearchableInfo(info); return true; }
Most apps that provide collections of data need some way of searching through that data. This is not just adding a filter to a list, but rather searching for a piece of data across the entire app. For example, if we are making a social network app, we may want to search through the people, places, and events from a single location.
The first thing we need is an activity that will be responsible for responding to search requests. This activity is not special in any way, except that there should be some code that handles the search intent. Intents are used by Android to pass data between various apps and activities.
To do this, we check the Action
property of the activity's Intent
property. If we are searching from another activity, the Intent
property can be read from the OnCreate()
method. If we are searching from the search activity, the activity is not reconstructed, so we read the Intent
property from the parameter in the OnNewIntent()
method.
In order to obtain the search query from the Intent
property, we use the GetStringExtra()
method and pass in the SearchManager.Query
value. This will return a string representing what the user requested.
Once we have the search activity set up, we need to add various attributes that describe how the activity will receive the search intent. We add an [ActionFilter]
attribute, with a value of ActionSearch
that lets Android know that we wish to receive the search intents. We also add a [MetaData]
attribute that points to the configuration of the search activity. This configuration is a simple XML resource that describes various properties, such as the hint in the search box or whether voice search is enabled.
Xamarin.Android tries to avoid any possible naming conflicts when it merges all the types in the assemblies by hashing the generated namespaces. To avoid our search activity being lost with a random name, we use the [Register]
attribute to give it a specific name.
Now that our search activity is complete, we need to let Android know which activities can make use of the search activity. This is done by adding a [MetaData]
attribute to the various activities, passing in the full name of the search activity.
We can either attribute each activity to use the search activity or we can apply a global attribute for all activities. We can also use a combination of both, allowing us to override the global attribute for a specific activity.
If we want to start a search, we have several options depending on what version of Android we are targeting. We can make use of the search dialog or overlay for all versions of Android. This is simply requested by invoking the OnSearchRequested()
method from any activity.
If we are targeting Android version 3.0 and later; we can make use of the SearchView
option in the action bar. This also is available to the older versions of Android if we are using the support libraries.
When we inflate the action bar, we need to ensure that we attach the search configuration to the search view, as we did with the [MetaData]
attribute on the search activity.
If we are supporting Android versions 4.1 and later, we can integrate with Google Now. First, we add a new [IntentFilter]
attribute to the search activity:
[IntentFilter( new[] { "com.google.android.gms.actions.SEARCH_ACTION" }, Categories = new[] { Intent.CategoryDefault })]
Then, we need to extend our intent handling, such as in the OnCreate()
method, to accept the new intent from Google Now:
if (intent.Action == Intent.ActionSearch ||
intent.Action == "com.google.android.gms.actions.SEARCH_ACTION") {
string query = intent.GetStringExtra(SearchManager.Query);
}
In order to test if our integration with Google Now is working, we have to first publish our app to the Play Store. If we download our app and then search for something like: "Ok Google, search for <search query> in <app package name>
".
However, if we wish to make sure that our app launches correctly, we can launch it from the command line using ADB:
adb shell am start -a com.google.android.gms.actions.SEARCH_ACTION -e query "<search query>" <app package name here>