search sample code

Credits

This code is taken from API Demos as a quick reference.

satya - Saturday, October 10, 2009 11:26:25 AM

search_invoke.xml


<!-- This activity exercises search invocation options -->
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">
    
    <!--  Section: Information -->
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="4dip"
        android:text="@string/msg_search" />
    
     <!--  Section: Invocation -->
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/search_sect_invocation" />

    <Button android:id="@+id/btn_start_search"
        android:text="@string/label_onsearchrequested"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
            
    <Spinner android:id="@+id/spinner_menu_mode"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:drawSelectorOnTop="true" />
            
    <!--  Section: Options -->
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/search_sect_options" />
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:orientation="horizontal">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/label_search_query_prefill" />
            <EditText android:id="@+id/txt_query_prefill"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:maxEms="10"
                android:minEms="10" />
        </LinearLayout>
    </LinearLayout>
    
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:orientation="horizontal">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/label_search_query_appdata" />
            <EditText android:id="@+id/txt_query_appdata"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:maxEms="10"
                android:minEms="10" />
        </LinearLayout>
    </LinearLayout>
 </LinearLayout>

satya - Saturday, October 10, 2009 11:35:04 AM

searchinvoke.java


public class SearchInvoke extends Activity
{  
    // UI elements
    Button mStartSearch;
    Spinner mMenuMode;
    EditText mQueryPrefill;
    EditText mQueryAppData;
    
    // Menu mode spinner choices
    // This list must match the list found in samples
   // /ApiDemos/res/values/arrays.xml
    final static int MENUMODE_SEARCH_KEY = 0;
    final static int MENUMODE_MENU_ITEM = 1;
    final static int MENUMODE_TYPE_TO_SEARCH = 2;
    final static int MENUMODE_DISABLED = 3;
    
    /** 
     * Called with the activity is first created.
     * 
     *  We aren't doing anything special in this 
    *  implementation, other than
     *  the usual activity setup code. 
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // Inflate our UI from its XML layout description.
        setContentView(R.layout.search_invoke);
        
        // Get display items for later interaction
        mStartSearch = (Button) findViewById(R.id.btn_start_search);
        mMenuMode = (Spinner) findViewById(R.id.spinner_menu_mode);
        mQueryPrefill = (EditText) findViewById(R.id.txt_query_prefill);
        mQueryAppData = (EditText) findViewById(R.id.txt_query_appdata);
        
        // Populate items
        ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
                            this, R.array.search_menuModes, 
                     android.R.layout.simple_spinner_item);
        adapter.setDropDownViewResource(
               android.R.layout.simple_spinner_dropdown_item);
        mMenuMode.setAdapter(adapter);
        
        // Create listener for the menu mode dropdown.  
      // We use this to demonstrate control
        // of the default keys handler in every Activity.  
      // More typically, you will simply set
        // the default key mode in your activity's onCreate() handler.
        mMenuMode.setOnItemSelectedListener(
            new OnItemSelectedListener() {
                public void onItemSelected(
                        AdapterView<?> parent, View view, int position, long id) {
                    if (position == MENUMODE_TYPE_TO_SEARCH) {
                        setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
                    } else {
                        setDefaultKeyMode(DEFAULT_KEYS_DISABLE);
                    }
                }

                public void onNothingSelected(AdapterView<?> parent) {
                    setDefaultKeyMode(DEFAULT_KEYS_DISABLE);
                }
            });
        
        // Attach actions to buttons
        mStartSearch.setOnClickListener(
            new OnClickListener() {
                public void onClick(View v) {
                    onSearchRequested();
                }
            });
    }
    
    /** 
     * Called when your activity's options menu needs to be updated. 
     */
    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        super.onPrepareOptionsMenu(menu);
        MenuItem item;
        
            // first, get rid of our menus (if any)
        menu.removeItem(0);
        menu.removeItem(1);
        
            // next, add back item(s) based on current menu mode
        switch (mMenuMode.getSelectedItemPosition())
        {
        case MENUMODE_SEARCH_KEY:
            item = menu.add( 0, 0, 0, "(Search Key)");
            break;
            
        case MENUMODE_MENU_ITEM:
            item = menu.add( 0, 0, 0, "Search");
            item.setAlphabeticShortcut(SearchManager.MENU_KEY);
            break;
            
        case MENUMODE_TYPE_TO_SEARCH:
            item = menu.add( 0, 0, 0, "(Type-To-Search)");
            break;
            
        case MENUMODE_DISABLED:
            item = menu.add( 0, 0, 0, "(Disabled)");
            break;
        }
        
        item = menu.add(0, 1, 0, "Clear History");
        return true;
    }
    
    /** Handle the menu item selections */
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case 0:
            switch (mMenuMode.getSelectedItemPosition()) {
            case MENUMODE_SEARCH_KEY:
                new AlertDialog.Builder(this)
                    .setMessage(
               "To invoke search, dismiss this dialog "
               + and press the search key" +
                            " (F5 on the simulator).")
                    .setPositiveButton("OK", null)
                    .show();
                break;
                
            case MENUMODE_MENU_ITEM:
                onSearchRequested();
                break;
                
            case MENUMODE_TYPE_TO_SEARCH:
                new AlertDialog.Builder(this)
                    .setMessage("To invoke search, dismiss "
                    + "this dialog and start typing.")
                    .setPositiveButton("OK", null)
                    .show();
                break;
                
            case MENUMODE_DISABLED:
                new AlertDialog.Builder(this)
                    .setMessage("You have disabled search.")
                    .setPositiveButton("OK", null)
                    .show();
                break;
            }
            break;
        case 1:
            clearSearchHistory();
            break;
        }
    
         return super.onOptionsItemSelected(item);
    }
    
    /**
     * This hook is called when the user signals 
     * the desire to start a search.
     * 
     * By overriding this hook we can insert local 
     * or context-specific data.
     * 
     * @return Returns true if search launched, 
     * false if activity blocks it
     */
    @Override
    public boolean onSearchRequested() {
        // If your application absolutely must 
        // disable search, do it here.
        if (mMenuMode.getSelectedItemPosition() == MENUMODE_DISABLED) {
            return false;
        }
        
        // It's possible to prefill the query string 
        // before launching the search
        // UI.  For this demo, we simply copy it 
        // from the user input field.
        // For most applications, you can simply pass 
        // null to startSearch() to
        // open the UI with an empty query string.
        final String queryPrefill = 
          mQueryPrefill.getText().toString();
        
        // Next, set up a bundle to send context-specific 
        // search data (if any)
        // The bundle can contain any number of elements, 
        // sing any number of keys;
        // For this Api Demo we copy a string from the user 
        // input field, and store
        // it in the bundle as a string with the key "demo_key".
        // For most applications, you can simply pass 
        // null to startSearch().
        Bundle appDataBundle = null;
        final String queryAppDataString = 
         mQueryAppData.getText().toString();
        if (queryAppDataString != null) {
            appDataBundle = new Bundle();
            appDataBundle.putString("demo_key", queryAppDataString);
        }
        
        // Now call the Activity member function that 
        // invokes the Search Manager UI.
        startSearch(queryPrefill, false, appDataBundle, false); 
        
        // Returning true indicates that we did launch 
        // the search, instead of blocking it.
        return true;
    }
    
    /**
     * Any application that implements search suggestions 
     * based on previous actions (such as
     * recent queries, page/items viewed, etc.) should 
     * provide a way for the user to clear the
     * history.  This gives the user a measure of privacy, 
     * if they do not wish for their recent
     * searches to be replayed by other users of 
     * the device (via suggestions).
     * 
     * This example shows how to clear the search history 
     * for apps that use 
     * android.provider.SearchRecentSuggestions.  
     * If you have developed a custom suggestions
     * provider, you'll need to provide a similar 
     * API for clearing history.
     * 
     * In this sample app we call this method from a 
     * "Clear History" menu item.  You could also 
     * implement the UI in your preferences, or any 
     * other logical place in your UI.
     */
    private void clearSearchHistory() {
        SearchRecentSuggestions suggestions = 
         new SearchRecentSuggestions(this, 
            SearchSuggestionSampleProvider.AUTHORITY, 
            SearchSuggestionSampleProvider.MODE);
        suggestions.clearHistory();
    }
    
}

satya - Saturday, October 10, 2009 11:37:42 AM

searchqueryresults.java


public class SearchQueryResults extends Activity
{  
    // UI elements
    TextView mQueryText;
    TextView mAppDataText;
    TextView mDeliveredByText;
    
    /** Called with the activity is first created.
    * 
    *  After the typical activity setup code, 
   * we check to see if we were launched
    *  with the ACTION_SEARCH intent, and if so, we handle it.
    */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // Inflate our UI from its XML layout description.
        setContentView(R.layout.search_query_results);
        
        // Get active display items for later updates
        mQueryText = (TextView) findViewById(R.id.txt_query);
        mAppDataText = (TextView) findViewById(R.id.txt_appdata);
        mDeliveredByText = (TextView) findViewById(R.id.txt_deliveredby);
        
        // get and process search query here
        final Intent queryIntent = getIntent();
        final String queryAction = queryIntent.getAction();
        if (Intent.ACTION_SEARCH.equals(queryAction)) {
            doSearchQuery(queryIntent, "onCreate()");
        }
        else {
            mDeliveredByText.setText("onCreate(), but no ACTION_SEARCH intent");
        }
    }
    
    /** 
     * Called when new intent is delivered.
     *
     * This is where we check the incoming intent for a query string.
     * 
     * @param newIntent The intent used to restart this activity
     */
    @Override
    public void onNewIntent(final Intent newIntent) {
        super.onNewIntent(newIntent);
        
        // get and process search query here
        final Intent queryIntent = getIntent();
        final String queryAction = queryIntent.getAction();
        if (Intent.ACTION_SEARCH.equals(queryAction)) {
            doSearchQuery(queryIntent, "onNewIntent()");
        }
        else {
            mDeliveredByText.setText("onNewIntent(), 
             but no ACTION_SEARCH intent");
        }
    }
    
    /**
     * Generic search handler.
     * 
     * In a "real" application, you would use the 
     * query string to select results from
     * your data source, and present a list of 
     * those results to the user.
     */
    private void doSearchQuery(final Intent queryIntent, 
                        final String entryPoint) {
        
        // The search query is provided as an 
        // "extra" string in the query intent
        final String queryString = 
          queryIntent.getStringExtra(SearchManager.QUERY);
        mQueryText.setText(queryString);
        
        // Record the query string in the recent 
        // queries suggestions provider.
        SearchRecentSuggestions suggestions = 
          new SearchRecentSuggestions(this, 
                SearchSuggestionSampleProvider.AUTHORITY, 
            SearchSuggestionSampleProvider.MODE);
        suggestions.saveRecentQuery(queryString, null);
        
        // If your application provides context data for its searches, 
        // you will receive it as an "extra" bundle in the query intent. 
        // The bundle can contain any number of elements, 
        // using any number of keys;
        // For this Api Demo we're just using a single string, 
        // stored using "demo key".
        final Bundle appData = queryIntent.getBundleExtra(
                  SearchManager.APP_DATA);
        if (appData == null) {
            mAppDataText.setText("<no app data bundle>");
        }
        if (appData != null) {
            String testStr = appData.getString("demo_key");
            mAppDataText.setText((testStr == null) 
             ? "<no app data>" : testStr);
        }
        
        // Report the method by which we were called.
        mDeliveredByText.setText(entryPoint);
    }
}

satya - Saturday, October 10, 2009 11:39:16 AM

corresponding layout search_results.xml


<!-- This activity displays search queries and "results" -->
<LinearLayout xmlns:android=
   "http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">
    
    <!--  Section: Information -->
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="4dip"
        android:text="@string/msg_search_results" />
    
    <!--  Section: Search Query String -->
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/label_search_query" />
        <TextView android:id="@+id/txt_query"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>

    <!--  Section: Search Query application context data -->
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/label_search_appdata" />
        <TextView android:id="@+id/txt_appdata"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>

    <!--  Section: How the query was delivered -->
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/label_search_deliveredby" />
        <TextView android:id="@+id/txt_deliveredby"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>

 </LinearLayout>

satya - Saturday, October 10, 2009 11:41:58 AM

SearchSuggestions Provider


import android.content.SearchRecentSuggestionsProvider;

/**
 * To create a search suggestions provider using 
 * the built-in recent queries mode, 
 * simply extend SearchRecentSuggestionsProvider as 
 * shown here, and configure with
 * a unique authority and the mode you with to use.  
 * For more information, see
 * {@link android.content.SearchRecentSuggestionsProvider}.
 */
public class SearchSuggestionSampleProvider 
extends SearchRecentSuggestionsProvider {
    
    /**
     * This is the provider authority identifier.  
     * The same string must appear in your
     * Manifest file, and any time you instantiate a 
     * {@link android.provider.SearchRecentSuggestions} 
     * helper class. 
     */
    final static String AUTHORITY = 
      "com.example.android.apis.SuggestionProvider";
    /**
     * These flags determine the operating mode of the 
     * suggestions provider.  This value should 
     * not change from run to run, because when it 
     * does change, your suggestions database may 
     * be wiped.
     */
    final static int MODE = DATABASE_MODE_QUERIES;
    
    /**
     * The main job of the constructor is to 
     * call {@link #setupSuggestions(String, int)} with the
     * appropriate configuration values.
     */
    public SearchSuggestionSampleProvider() {
        super();
        setupSuggestions(AUTHORITY, MODE);
    }
}

satya - Saturday, October 10, 2009 11:43:11 AM

Here is the api ref for SearchRecentSuggestionsProvider

Here is the api ref for SearchRecentSuggestionsProvider

satya - Wednesday, October 14, 2009 3:32:59 PM

Defining the invoke activity in the manifest file


<activity android:name=".app.SearchInvoke"
        android:label="@string/search_invoke">
   <intent-filter>
      <action android:name="android.intent.action.MAIN" />
      <category android:name="android.intent.category.SAMPLE_CODE" />
   </intent-filter>

   <!--
   **************************************************************
   * This metadata entry causes 
   * .app.SearchQueryResults to be the default context 
   * whenever the user invokes search while in this Activity.
   **************************************************************
   -->
   <meta-data android:name="android.app.default_searchable"
            android:value=".app.SearchQueryResults" />
   
   <!--
   **************************************************************
   * This is not the typical way to define 
   * android.app.default_searchable
   * and we show it here only because we wish to 
   * confine the search demo to this
   * section of the ApiDemos application.
   *
   * For typical applications, it's simpler to define 
   * android.app.default_searchable
   * just once, at the application level, where it 
   * serves as a default for all of 
   * the Activities in your package.
   **************************************************************
   -->
   
</activity>

satya - Wednesday, October 14, 2009 3:33:27 PM

Same thing defluffed


<activity android:name=".app.SearchInvoke"
        android:label="@string/search_invoke">
   <intent-filter>
      <action android:name="android.intent.action.MAIN" />
      <category android:name="android.intent.category.SAMPLE_CODE" />
   </intent-filter>

   <meta-data android:name="android.app.default_searchable"
            android:value=".app.SearchQueryResults" />
</activity>

satya - Wednesday, October 14, 2009 3:37:07 PM

Same thing at an application level...


<application>
   <meta-data android:name="android.app.default_searchable"
            android:value=".app.SearchQueryResults" />
<!-- ....activities, providers etc... -->			
</application>

satya - Wednesday, October 14, 2009 3:37:20 PM

of course, activity overrides the application...

of course, activity overrides the application...

satya - Wednesday, October 14, 2009 3:41:13 PM

Manifest entry for a search activity


<!-- 
This activity represents the "search" 
activity in your application, in which 
-->

<!-- search results are gathered and displayed. -->

<activity android:name=".app.SearchQueryResults"
        android:label="@string/search_query_results">
   <intent-filter>
      <action android:name="android.intent.action.MAIN" />
      <category android:name="android.intent.category.SAMPLE_CODE" />
   </intent-filter>
   
   <!-- 
   This intent-filter identifies 
   this activity as "searchable" 
   -->
   
   <intent-filter>
      <action android:name="android.intent.action.SEARCH" />
      <category android:name="android.intent.category.DEFAULT" />
   </intent-filter>
   
   <!-- 
   This metadata entry provides further 
   configuration details for searches 
   -->
   
   <!-- that are handled by this activity. -->
   
   <meta-data android:name="android.app.searchable"
            android:resource="@xml/searchable" />
</activity>

satya - Wednesday, October 14, 2009 3:43:47 PM

Here is the xml/searchable file for this activity


<!-- 
The attributes in this XML file 
provide configuration information 
for the Search Manager. 
-->
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
    android:label="@string/search_label"
    android:hint="@string/search_hint" 
    android:searchMode="showSearchLabelAsBadge"
    
    android:voiceSearchMode="showVoiceSearchButton|launchRecognizer"
    android:voiceLanguageModel="free_form"
    android:voicePromptText="@string/search_invoke"

    android:searchSuggestAuthority="com.example.android.apis.SuggestionProvider"
    android:searchSuggestSelection=" ? "
/>

satya - Wednesday, October 14, 2009 3:44:10 PM

Here is what the string values are


<string name="search_label">Search Demo</string>
<string name="search_hint">Search Demo Hint</string>

satya - Wednesday, October 14, 2009 3:46:49 PM

Here are all the strings


<!-- ============================== -->
<!--  app/search examples strings  -->
<!-- ============================== -->

<string name="search_invoke">App/Search/Invoke Search</string>

<string name="msg_search">
   This activity shows a few different ways to invoke search, 
   and inserts context-specific data for use by the search activity.
</string>

<string name="search_sect_invocation">Ways to invoke search</string>
<string name="label_onsearchrequested">onSearchRequested()</string>
<string name="search_sect_options">Optional search parameters</string>
<string name="label_search_query_prefill">"Prefill query: "</string>
<string name="label_search_query_appdata">"App Data: "</string>

<string name="search_query_results">App/Search/Query Search Results</string>

<string name="msg_search_results">
   This activity accepts query strings via the ACTION_SEARCH intent.  
   In a full implementation, you would use the query string to 
   select results from your data source, and present a list 
   of those results to the user.
</string>

<string name="label_search_query">"Query String: "</string>
<string name="label_search_appdata">"Query App Data: "</string>
<string name="label_search_deliveredby">"Activity Method: "</string>

<string name="search_label">Search Demo</string>
<string name="search_hint">Search Demo Hint</string>

satya - Friday, October 16, 2009 11:22:48 AM

Here is an activity that is both invoking and also searching


public class SearchActivity extends Activity 
{
   private final static String tag ="SearchActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Log.d(tag,"I am being created");
        //otherwise do this
        setContentView(R.layout.layout_test_search_activity);
       this.setDefaultKeyMode(Activity.DEFAULT_KEYS_SEARCH_LOCAL);
        return;
    }
    
    @Override
    public void onNewIntent(final Intent newIntent) 
    {
        super.onNewIntent(newIntent);
        Log.d(tag,"new intent calling me");
        
        // get and process search query here
        final Intent queryIntent = getIntent();
        final String queryAction = queryIntent.getAction();
        if (Intent.ACTION_SEARCH.equals(queryAction)) 
        {
           Log.d(tag,"new intent for search");
        }
        else {
           Log.d(tag,"new intent NOT for search");
        }
    }
 }

satya - Friday, October 16, 2009 1:37:34 PM

type-to-search is enabled by the setDefaultKeyMode

By setting this default key mode, android will interpret any key stroke as a suggestion to search and opens up the search dialog on top of this results activity.

This trick essentially makes the same activity to respond to search results and also invoke the search activity.

This may be the only case where "singleTop" and "onNewIntent" makes sense. Otherwise you will not see the "onNewIntent" called. This is because when you press the back button on the search results activity (the one that is responding to search) to reach the activity that started the search then the "results activity" is no longer on the top of the stack. "onSingleTop" has no effect if that activity is not on the top.

satya - Tuesday, October 20, 2009 2:25:11 PM

Defining a provider


<provider android:name=".TestSuggestionProvider"
       android:authorities=
        "com.ai.android.search.TestSuggestionProvider" />

satya - Friday, October 23, 2009 2:17:38 PM

Another searchable.xml


<searchable xmlns:android="http://schemas.android.com/apk/res/android"
    android:label="@string/search_label"
    android:hint="@string/search_hint" 
    android:searchMode="showSearchLabelAsBadge"
    android:searchSettingsDescription="suggests urls"
    
    android:searchSuggestAuthority="com.ai.android.search.suggesturlprovider"
    android:searchSuggestIntentAction="android.intent.action.VIEW"
    android:searchSuggestSelection=" ? "
/>