Integrating app searchability

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.

How to do it...

Integrating search into our app is very easy, only requiring a search activity and a few attributes on the activities:

  1. In order to handle searching, our app has to have an activity that will handle search intents:
    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);
        }
      }
    }
  2. Because Xamarin.Android will hash the type namespaces when it generates the Java code, we have to let it know what name we want for the search activity using the [Register] attribute:
    [Register("com.xamarincookbook.SearchActivity")]
  3. Once we have the search activity, we need to add attributes to it to let Android know that it can accept search intents:
    [IntentFilter(new[] { Intent.ActionSearch })]
  4. We now have to create an XML file, in the 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>
  5. The XML file is associated with the search activity using an attribute with the key 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:

  1. We need to let the other activities know where to send their search intents. This can be done in two ways, one of which is to add an attribute on each activity, as follows:
    [MetaData(
      "android.app.default_searchable", 
      Value = "com.xamarincookbook.SearchActivity")]
  2. Or, add an assembly-level attribute for all activities:
    [assembly: MetaData(
      "android.app.default_searchable",
      Value = "com.xamarincookbook.SearchActivity")]
  3. Next, we add the search view to the menu resource, where we are using the support library to provide the widget:
    <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" />
  4. When we inflate the action bar, we need to let the search view know about our search properties, ensuring that we cast the 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;
    }

How it works...

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.

Note

The search query is read out of the Intent property extras using the SearchManager.Query key.

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.

Note

The [ActionFilter] attributes are used to let the Android OS know which intents an app can receive and understand.

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.

Tip

The new SearchView action bar item can be used in older Android versions through 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.

There's more...

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

Tip

Integrating with Google Now requires that the app support the additional intent.

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>
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset