Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 363d178f authored by Robert Ly's avatar Robert Ly Committed by Android Git Automerger
Browse files

am b285125d: Merge "docs: android u search class" into ics-mr1

* commit 'b285125d':
  docs: android u search class
parents 6513bceb b285125d
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -98,6 +98,26 @@
        </ul>
      </li>

      <li class="toggle-list">
        <div><a href="<?cs var:toroot ?>training/search/index.html">
            <span class="en">Adding Search Functionality</span>
          </a></div>
        <ul>
          <li><a href="<?cs var:toroot ?>training/search/setup.html">
            <span class="en">Setting up the Search Interface</span>
          </a>
          </li>
          <li><a href="<?cs var:toroot ?>training/search/search.html">
            <span class="en">Storing and Searching for Data</span>
          </a>
          </li>
          <li><a href="<?cs var:toroot ?>training/search/backward-compat.html">
            <span class="en">Remaining Backward Compatible</span>
          </a>
          </li>
        </ul>
      </li>

      <li class="toggle-list">
        <div><a href="<?cs var:toroot ?>training/id-auth/index.html">
            <span class="en">Remembering Users</span>
+87 −0
Original line number Diff line number Diff line
page.title=Remaining Backward Compatible
trainingnavtop=true
previous.title=Storing and Searching for Data
previous.link=search.html 

@jd:body

  <div id="tb-wrapper">
    <div id="tb">
      <h2>This lesson teaches you to</h2>

      <ul>
        <li><a href="{@docRoot}training/search/backward-compat.html#set-sdk">Set Minimum and Target
        SDK Values</a></li>

        <li><a href="{@docRoot}training/search/backward-compat.html#provide-sd">Provide the Search
        Dialog for Older Devices</a></li>

        <li><a href="{@docRoot}training/search/backward-compat.html#check-ver">Check the Android Build
        Version at Runtime</a></li>
      </ul>
    </div>
  </div>

  <p>The {@link android.widget.SearchView} and action bar are only available on Android 3.0 and
  later. To support older platforms, you can fall back to the search dialog. The search dialog is a
  system provided UI that overlays on top of your application when invoked.</p>

  <h2 id="set-sdk">Set Minimum and Target API levels</h2>

  <p>To setup the search dialog, first declare in your manifest that you want to support older
  devices, but want to target Android 3.0 or later versions. When you do this, your application
  automatically uses the action bar on Android 3.0 or later and uses the traditional menu system on
  older devices:</p>
  <pre>
&lt;uses-sdk android:minSdkVersion="7" android:targetSdkVersion="15" /&gt;

&lt;application&gt;
...
</pre>

  <h2 id="provide-sd">Provide the Search Dialog for Older Devices</h2>

  <p>To invoke the search dialog on older devices, call {@link
  android.app.Activity#onSearchRequested onSearchRequested()} whenever a user selects the search
  menu item from the options menu. Because Android 3.0 and higher devices show the
  {@link android.widget.SearchView} in the action bar (as demonstrated in the first lesson), only versions
  older than 3.0 call {@link android.app.Activity#onOptionsItemSelected onOptionsItemSelected()} when the
  user selects the search menu item.
  </p>
  <pre>
&#64;Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.search:
            onSearchRequested();
            return true;
        default:
            return false;
    }
}
</pre>

  <h2 id="check-ver">Check the Android Build Version at Runtime</h2>

  <p>At runtime, check the device version to make sure an unsupported use of {@link
  android.widget.SearchView} does not occur on older devices. In our example code, this happens in
  the {@link android.app.Activity#onCreateOptionsMenu onCreateOptionsMenu()} method:</p>
  <pre>
&#64;Override
public boolean onCreateOptionsMenu(Menu menu) {

    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.options_menu, menu);

    if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.HONEYCOMB) {
        SearchManager searchManager =
                (SearchManager) getSystemService(Context.SEARCH_SERVICE);
        SearchView searchView =
                (SearchView) menu.findItem(R.id.search).getActionView();
        searchView.setSearchableInfo(
                searchManager.getSearchableInfo(getComponentName()));
        searchView.setIconifiedByDefault(false);
    }
    return true;
}
</pre>
+53 −0
Original line number Diff line number Diff line
page.title=Adding Search Functionality
trainingnavtop=true
startpage=true
next.title=Setting Up the Search Interface
next.link=setup.html

@jd:body

  <div id="tb-wrapper">
    <div id="tb">
      <h2>Dependencies and prerequisites</h2>

      <ul>
        <li>Android 3.0 or later (with some support for Android 2.1)</li>

        <li>Experience building an Android <a href="{@docRoot}guide/topics/ui/index.html">User
        Interface</a></li>
      </ul>

      <h2>You should also read</h2>

      <ul>
        <li><a href="{@docRoot}guide/topics/search/index.html">Search</a></li>

        <li><a href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable
        Dictionary Sample App</a></li>
      </ul>
    </div>
  </div>

  <p>Android's built-in search features offer apps an easy way to provide a
  consistent search experience for all users. There are two ways to implement search in your app
  depending on the version of Android that is running on the device. This class covers how to add
  search with {@link android.widget.SearchView}, which was introduced in Android 3.0, while
  maintaining backward compatibility with older versions of Android by using the default search
  dialog provided by the system.</p>

  <h2>Lessons</h2>

  <dl>
    <dt><b><a href="setup.html">Setting Up the Search Interface</a></b></dt>

    <dd>Learn how to add a search interface to your app and how to configure an activity to handle
    search queries.</dd>

    <dt><b><a href="search.html">Storing and Searching for Data</a></b></dt>

    <dd>Learn a simple way to store and search for data in a SQLite virtual database table.</dd>

    <dt><b><a href="backward-compat.html">Remaining Backward Compatible</a></b></dt>

    <dd>Learn how to keep search features backward compatible with older devices by using.</dd>
  </dl>
 No newline at end of file
+217 −0
Original line number Diff line number Diff line
page.title=Storing and Searching for Data
trainingnavtop=true
previous.title=Setting Up the Search Interface
previous.link=setup.html
next.title=Remaining Backward Compatible
next.link=backward-compat.html

@jd:body

  <div id="tb-wrapper">
    <div id="tb">
      <h2>This lesson teaches you to</h2>

      <ul>
        <li><a href="{@docRoot}training/search/search.html#create">Create the Virtual
        Table</a></li>

        <li><a href="{@docRoot}training/search/search.html#populate">Populate the Virtual
        Table</a></li>

        <li><a href="{@docRoot}training/search/search.html#search">Search for the Query</a></li>
      </ul>
    </div>
  </div>

  <p>There are many ways to store your data, such as in an online database, in a local SQLite
  database, or even in a text file. It is up to you to decide what is the best solution for your
  application. This lesson shows you how to create a SQLite virtual table that can provide robust
  full-text searching. The table is populated with data from a text file that contains a word and
  definition pair on each line in the file.</p>

  <h2 id="create">Create the Virtual Table</h2>

  <p>A virtual table behaves similarly to a SQLite table, but reads and writes to an object in
  memory via callbacks, instead of to a database file. To create a virtual table, create a class
  for the table:</p>
  <pre>
public class DatabaseTable {
    private final DatabaseOpenHelper mDatabaseOpenHelper;

    public DatabaseTable(Context context) {
        mDatabaseOpenHelper = new DatabaseOpenHelper(context);
    }
}
</pre>

  <p>Create an inner class in <code>DatabaseTable</code> that extends {@link
  android.database.sqlite.SQLiteOpenHelper}. The {@link android.database.sqlite.SQLiteOpenHelper} class
  defines abstract methods that you must override so that your database table can be created and
  upgraded when necessary. For example, here is some code that declares a database table that will
  contain words for a dictionary app:</p>
  <pre>
public class DatabaseTable {

    private static final String TAG = "DictionaryDatabase";

    //The columns we'll include in the dictionary table
    public static final String COL_WORD = "WORD";
    public static final String COL_DEFINITION = "DEFINITION";

    private static final String DATABASE_NAME = "DICTIONARY";
    private static final String FTS_VIRTUAL_TABLE = "FTS";
    private static final int DATABASE_VERSION = 1;

    private final DatabaseOpenHelper mDatabaseOpenHelper;

    public DatabaseTable(Context context) {
        mDatabaseOpenHelper = new DatabaseOpenHelper(context);
    }

    private static class DatabaseOpenHelper extends SQLiteOpenHelper {

        private final Context mHelperContext;
        private SQLiteDatabase mDatabase;

        private static final String FTS_TABLE_CREATE =
                    "CREATE VIRTUAL TABLE " + FTS_VIRTUAL_TABLE +
                    " USING fts3 (" +
                    COL_WORD + ", " +
                    COL_DEFINITION + ")";

        DatabaseOpenHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
            mHelperContext = context;
        }

        &#64;Override
        public void onCreate(SQLiteDatabase db) {
            mDatabase = db;
            mDatabase.execSQL(FTS_TABLE_CREATE);
        }

        &#64;Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
                    + newVersion + ", which will destroy all old data");
            db.execSQL("DROP TABLE IF EXISTS " + FTS_VIRTUAL_TABLE);
            onCreate(db);
        }
    }
}
</pre>

  <h2 id="populate">Populate the Virtual Table</h2>

  <p>The table now needs data to store. The following code shows you how to read a text file
  (located in <code>res/raw/definitions.txt</code>) that contains words and their definitions, how
  to parse that file, and how to insert each line of that file as a row in the virtual table. This
  is all done in another thread to prevent the UI from locking. Add the following code to your
  <code>DatabaseOpenHelper</code> inner class.</p>

  <p class="note"><strong>Tip:</strong> You also might want to set up a callback to notify your UI
  activity of this thread's completion.</p>
  <pre>
private void loadDictionary() {
        new Thread(new Runnable() {
            public void run() {
                try {
                    loadWords();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }).start();
    }

private void loadWords() throws IOException {
    final Resources resources = mHelperContext.getResources();
    InputStream inputStream = resources.openRawResource(R.raw.definitions);
    BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));

    try {
        String line;
        while ((line = reader.readLine()) != null) {
            String[] strings = TextUtils.split(line, "-");
            if (strings.length &lt; 2) continue;
            long id = addWord(strings[0].trim(), strings[1].trim());
            if (id &lt; 0) {
                Log.e(TAG, "unable to add word: " + strings[0].trim());
            }
        }
    } finally {
        reader.close();
    }
}

public long addWord(String word, String definition) {
    ContentValues initialValues = new ContentValues();
    initialValues.put(COL_WORD, word);
    initialValues.put(COL_DEFINITION, definition);

    return mDatabase.insert(FTS_VIRTUAL_TABLE, null, initialValues);
}
</pre>

  <p>Call the <code>loadDictionary()</code> method wherever appropriate to populate the table. A
  good place would be in the {@link android.database.sqlite.SQLiteOpenHelper#onCreate onCreate()}
  method of the <code>DatabaseOpenHelper</code> class, right after you create the table:</p>
  <pre>
&#64;Override
public void onCreate(SQLiteDatabase db) {
    mDatabase = db;
    mDatabase.execSQL(FTS_TABLE_CREATE);
    loadDictionary();
}
</pre>

  <h2 id="search">Search for the Query</h2>

  <p>When you have the virtual table created and populated, use the query supplied by your {@link
  android.widget.SearchView} to search the data. Add the following methods to the
  <code>DatabaseTable</code> class to build a SQL statement that searches for the query:</p>
  <pre>
public Cursor getWordMatches(String query, String[] columns) {
    String selection = COL_WORD + " MATCH ?";
    String[] selectionArgs = new String[] {query+"*"};

    return query(selection, selectionArgs, columns);
}

private Cursor query(String selection, String[] selectionArgs, String[] columns) {
    SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
    builder.setTables(FTS_VIRTUAL_TABLE);

    Cursor cursor = builder.query(mDatabaseOpenHelper.getReadableDatabase(),
            columns, selection, selectionArgs, null, null, null);

    if (cursor == null) {
        return null;
    } else if (!cursor.moveToFirst()) {
        cursor.close();
        return null;
    }
    return cursor;
}
</pre>

  <p>Search for a query by calling <code>getWordMatches()</code>. Any matching results are returned
  in a {@link android.database.Cursor} that you can iterate through or use to build a {@link android.widget.ListView}.
  This example calls <code>getWordMatches()</code> in the <code>handleIntent()</code> method of the searchable
  activity. Remember that the searchable activity receives the query inside of the {@link
  android.content.Intent#ACTION_SEARCH} intent as an extra, because of the intent filter that you
  previously created:</p>
  <pre>
DatabaseTable db = new DatabaseTable(this);

...

private void handleIntent(Intent intent) {

    if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
        String query = intent.getStringExtra(SearchManager.QUERY);
        Cursor c = db.getWordMatches(query, null);
        //process Cursor and display results
    }
}
</pre>
 No newline at end of file
+197 −0
Original line number Diff line number Diff line
page.title=Setting Up the Search Interface
trainingnavtop=true
next.title=Storing and Searching for Data
next.link=search.html

@jd:body

  <div id="tb-wrapper">
    <div id="tb">
      <h2>This lesson teaches you to</h2>

      <ul>
        <li><a href="{@docRoot}training/search/setup.html#add-sv">Add the Search View to the Action
        Bar</a></li>

        <li><a href="{@docRoot}training/search/setup.html#create-sc">Create a Searchable
        Configuration</a></li>

        <li><a href="{@docRoot}training/search/setup.html#create-sa">Create a Searchable
        Activity</a></li>
      </ul>

      <h2>You should also read:</h2>

      <ul>
        <li><a href="{@docRoot}guide/topics/ui/actionbar.html">Action Bar</a></li>
      </ul>
    </div>
  </div>

  <p>Beginning in Android 3.0, using the {@link android.widget.SearchView} widget as an item in
  the action bar is the preferred way to provide search in your app. Like with all items in
  the action bar, you can define the {@link android.widget.SearchView} to show at all times, only
  when there is room, or as a collapsible action, which displays the {@link
  android.widget.SearchView} as an icon initially, then takes up the entire action bar as a search
  field when the user clicks the icon.</p>

  <p class="note"><strong>Note:</strong> Later in this class, you will learn how to make your
  app compatible down to Android 2.1 (API level 7) for devices that do not support
  {@link android.widget.SearchView}.</p>

  <h2 id="add-sv">Add the Search View to the Action Bar</h2>

  <p>To add a {@link android.widget.SearchView} widget to the action bar, create a file named
  <code>res/menu/options_menu.xml</code> in your project and add the following code to the file.
  This code defines how to create the search item, such as the icon to use and the title of the
  item. The <code>collapseActionView</code> attribute allows your {@link android.widget.SearchView}
  to expand to take up the whole action bar and collapse back down into a
  normal action bar item when not in use. Because of the limited action bar space on handset devices,
  using the <code>collapsibleActionView</code> attribute is recommended to provide a better
  user experience.</p>
  <pre>
&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;menu xmlns:android="http://schemas.android.com/apk/res/android"&gt;
    &lt;item android:id="@+id/search"
          android:title="@string/search_title"
          android:icon="@drawable/ic_search"
          android:showAsAction="collapseActionView|ifRoom"
          android:actionViewClass="android.widget.SearchView" /&gt;
&lt;/menu&gt;
</pre>

  <p class="note"><strong>Note:</strong> If you already have an existing XML file for your menu
  items, you can add the <code>&lt;item&gt;</code> element to that file instead.</p>

  <p>To display the {@link android.widget.SearchView} in the action bar, inflate the XML menu
  resource (<code>res/menu/options_menu.xml</code>) in the {@link
  android.app.Activity#onCreateOptionsMenu onCreateOptionsMenu()} method of your activity:</p>
  <pre>
&#64;Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.options_menu, menu);

    return true;
}
</pre>

  <p>If you run your app now, the {@link android.widget.SearchView} appears in your app's action
  bar, but it isn't functional. You now need to define <em>how</em> the {@link
  android.widget.SearchView} behaves.</p>

  <h2 id="create-sc">Create a Searchable Configuration</h2>

  <p>A <a href="http://developer.android.com/guide/topics/search/searchable-config.html">searchable
  configuration</a> defines how the {@link android.widget.SearchView} behaves and is defined in a
  <code>res/xml/searchable.xml</code> file. At a minimum, a searchable configuration must contain
  an <code>android:label</code> attribute that has the same value as the
  <code>android:label</code> attribute of the <a href="{@docRoot}guide/topics/manifest/application-element.html">&lt;application&gt;</a> or
  <a href="{@docRoot}guide/topics/manifest/activity-element.html">&lt;activity&gt;</a> element in your Android manifest.
  However, we also recommend adding an <code>android:hint</code> attribute to give the user an idea of what to enter into the search
  box:</p>
  <pre>
&lt;?xml version="1.0" encoding="utf-8"?&gt;

&lt;searchable xmlns:android="http://schemas.android.com/apk/res/android"
        android:label="@string/app_name"
        android:hint="@string/search_hint" /&gt;
</pre>

  <p>In your application's manifest file, declare a <a href="{@docRoot}guide/topics/manifest/meta-data-element.html">
  <code>&lt;meta-data&gt;</code></a> element that points to the <code>res/xml/searchable.xml</code> file,
  so that your application knows where to find it. Declare the element in an <code>&lt;activity&gt;</code>
  that you want to display the {@link android.widget.SearchView} in:</p>
  <pre>
&lt;activity ... &gt;
    ...
    &lt;meta-data android:name="android.app.searchable"
            android:resource="@xml/searchable" /&gt;

&lt;/activity&gt;
</pre>

  <p>In the {@link android.app.Activity#onCreateOptionsMenu onCreateOptionsMenu()} method that you
  created before, associate the searchable configuration with the {@link android.widget.SearchView}
  by calling {@link android.widget.SearchView#setSearchableInfo}:</p>
  <pre>
&#64;Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.options_menu, menu);

    // Associate searchable configuration with the SearchView
    SearchManager searchManager =
           (SearchManager) getSystemService(Context.SEARCH_SERVICE);
    SearchView searchView =
            (SearchView) menu.findItem(R.id.search).getActionView();
    searchView.setSearchableInfo(
            searchManager.getSearchableInfo(getComponentName()));

    return true;
}
</pre>

  <p>The call to {@link android.app.SearchManager#getSearchableInfo getSearchableInfo()} obtains a
  {@link android.app.SearchableInfo} object that is created from the searchable configuration XML
  file. When the searchable configuration is correctly associated with your {@link
  android.widget.SearchView}, the {@link android.widget.SearchView} starts an activity with the
  {@link android.content.Intent#ACTION_SEARCH} intent when a user submits a query. You now need an
  activity that can filter for this intent and handle the search query.</p>

  <h2 id="create-sa">Create a Searchable Activity</h2>

  <p>A {@link android.widget.SearchView} tries to start an activity with the {@link
  android.content.Intent#ACTION_SEARCH} when a user submits a search query. A searchable activity
  filters for the {@link android.content.Intent#ACTION_SEARCH} intent and searches for the query in
  some sort of data set. To create a searchable activity, declare an activity of your choice to
  filter for the {@link android.content.Intent#ACTION_SEARCH} intent:</p>
  <pre>
&lt;activity android:name=".SearchResultsActivity" ... &gt;
    ...
    &lt;intent-filter&gt;
        &lt;action android:name="android.intent.action.SEARCH" /&gt;
    &lt;/intent-filter&gt;
    ...
&lt;/activity&gt;
</pre>

  <p>In your searchable activity, handle the {@link android.content.Intent#ACTION_SEARCH} intent by
  checking for it in your {@link android.app.Activity#onCreate onCreate()} method.</p>

  <p class="note"><strong>Note:</strong> If your searchable activity launches in single top mode
  (<code>android:launchMode="singleTop"</code>), also handle the {@link
  android.content.Intent#ACTION_SEARCH} intent in the {@link android.app.Activity#onNewIntent
  onNewIntent()} method. In single top mode, only one instance of your activity is created and
  subsequent calls to start your activity do not create a new activity on the
  stack. This launch mode is useful so users can perform searches from the same activity
  without creating a new activity instance every time.</p>
  <pre>
public class SearchResultsActivity extends Activity {

    &#64;Override
    public void onCreate(Bundle savedInstanceState) {
        ...
        handleIntent(getIntent());
    }

    &#64;Override
    protected void onNewIntent(Intent intent) {
        ...
        handleIntent(intent);
    }

    private void handleIntent(Intent intent) {

        if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
            String query = intent.getStringExtra(SearchManager.QUERY);
            //use the query to search your data somehow
        }
    }
    ...
}
</pre>

  <p>If you run your app now, the {@link android.widget.SearchView} can accept the user's query and
  start your searchable activity with the {@link android.content.Intent#ACTION_SEARCH} intent. It
  is now up to you to figure out how to store and search your data given a query.</p>
 No newline at end of file