cherry-pick from master: 3c26ae869e24e6cd7c63dc44fc3f46221262e15f

new version of the Searchable Dictionary that saves all the words
in an SQLite Database and FTS3 table, then performs all look ups
using the content provider, plus various style modifications.
diff --git a/samples/SearchableDictionary/AndroidManifest.xml b/samples/SearchableDictionary/AndroidManifest.xml
index 93cd47b..ec82449 100644
--- a/samples/SearchableDictionary/AndroidManifest.xml
+++ b/samples/SearchableDictionary/AndroidManifest.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
 /*
-** Copyright 2009, The Android Open Source Project
+** Copyright 2010, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
@@ -22,12 +22,11 @@
     <uses-sdk android:minSdkVersion="4" />
 
     <application android:label="@string/app_name"
-            android:icon="@drawable/ic_dictionary">
+                 android:icon="@drawable/ic_dictionary">
 
-        <!-- The default activity of the app.  Can also display search results. -->
+        <!-- The default activity of the app; displays search results. -->
         <activity android:name=".SearchableDictionary"
-                android:label="@string/app_name"
-                android:theme="@android:style/Theme.NoTitleBar">
+                  android:theme="@android:style/Theme.NoTitleBar">
 
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -37,22 +36,26 @@
             <!-- Receives the search request. -->
             <intent-filter>
                 <action android:name="android.intent.action.SEARCH" />
-                <category android:name="android.intent.category.DEFAULT" />
+                <!-- No category needed, because the Intent will specify this class component-->
             </intent-filter>
 
             <!-- Points to searchable meta data. -->
             <meta-data android:name="android.app.searchable"
-                    android:resource="@xml/searchable"/>
+                       android:resource="@xml/searchable" />
+
         </activity>
 
         <!-- Displays the definition of a word. -->
         <activity android:name=".WordActivity"
-                android:theme="@android:style/Theme.NoTitleBar"/>
+                  android:theme="@android:style/Theme.NoTitleBar" />
 
         <!-- Provides search suggestions for words and their definitions. -->
-        <provider android:name="DictionaryProvider"
-                android:authorities="dictionary"
-                android:syncable="false" />
+        <provider android:name=".DictionaryProvider"
+                  android:authorities="com.example.android.searchabledict.DictionaryProvider" />
+
+        <!-- Points to searchable activity so the whole app can invoke search. -->
+        <meta-data android:name="android.app.default_searchable"
+                   android:value=".SearchableDictionary" />
 
     </application>
 </manifest>
diff --git a/samples/SearchableDictionary/_index.html b/samples/SearchableDictionary/_index.html
index de3345e..6f9d997 100644
--- a/samples/SearchableDictionary/_index.html
+++ b/samples/SearchableDictionary/_index.html
@@ -1,21 +1,32 @@
 <p>A sample application that demonstrates Android's search framework.</p>
 
-<p>This application includes a dictionary of words. By invoking a search inside the app
-(via the device search button or Menu > Search), a local search will be performed
+<p>This application includes a dictionary of words. By invoking the Android search dialog inside the
+app (via the device search button or Menu > Search), you can perform a search
 across the dictionary. As you type, suggestions will appear, which you can select
-to view the full definition. You can also execute the search to view all word definitions
+to view the complete definition. You can also execute the search to view all word definitions
 that match the entered text.</p>
 
 <p>The application also grants content provider privileges to 
 Quick Search Box, Android's system-wide search tool. This means that the
 dictionary definitions can be offered as search suggestions outside of the application,
 when text is entered into Quick Search Box.</p>
-	
+
+<p>The code in this application demonstrates how to:</p>
+<ul>
+  <li>Implement a search interface using Android's search framework</li>
+  <li>Provide custom search suggestions and offer them in Quick Search Box</li>
+  <li>Create an FTS3 table in SQLite and perform full-text search</li>
+  <li>Create a content provider to perform all searches and queries to the dictionary</li>
+  <li>Use <a
+href="../../../reference/android/widget/SimpleCursorAdapter.html">SimpleCursorAdapter</a> to bind
+data from a Cursor to a ListView.</li>
+</ul>
+
 <p>See also:</p>
 <ul>
-  <li><a href="../../../reference/android/app/SearchManager.html">SearchManager</a></li>
-  <li><a href="../../../reference/android/content/ContentProvider.html">ContentProvider</a></li>
+  <li><a href="../../../guide/topics/search/index.html">Search Developer Guide</a></li>
 </ul>
 
+
 <img src="../images/SearchableDictionary1.png" />
 <img src="../images/SearchableDictionary2.png" />
\ No newline at end of file
diff --git a/samples/SearchableDictionary/res/drawable-hdpi/ic_menu_search.png b/samples/SearchableDictionary/res/drawable-hdpi/ic_menu_search.png
new file mode 100644
index 0000000..f78234e
--- /dev/null
+++ b/samples/SearchableDictionary/res/drawable-hdpi/ic_menu_search.png
Binary files differ
diff --git a/samples/SearchableDictionary/res/drawable-mdpi/ic_menu_search.png b/samples/SearchableDictionary/res/drawable-mdpi/ic_menu_search.png
new file mode 100644
index 0000000..94446db
--- /dev/null
+++ b/samples/SearchableDictionary/res/drawable-mdpi/ic_menu_search.png
Binary files differ
diff --git a/samples/SearchableDictionary/res/layout/main.xml b/samples/SearchableDictionary/res/layout/main.xml
index 666416d..5a7e969 100644
--- a/samples/SearchableDictionary/res/layout/main.xml
+++ b/samples/SearchableDictionary/res/layout/main.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
 /*
-** Copyright 2009, The Android Open Source Project
+** Copyright 2010, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
@@ -16,22 +16,25 @@
 ** limitations under the License.
 */
 -->
+<!-- Layout for SearchableActivity.
+ -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:orientation="vertical"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent">
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent">
     <TextView
-            android:id="@+id/textField"
-            android:textAppearance="?android:attr/textAppearanceMedium"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:text="@string/search_instructions"/>
-
+            android:id="@+id/text"
+            android:textColor="?android:textColorPrimary"
+            android:textSize="17dp"
+            android:text="@string/search_instructions"
+            android:background="@android:drawable/title_bar"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content" />
     <ListView
             android:id="@+id/list"
-            android:layout_width="match_parent"
-            android:layout_height="0dip"
-            android:layout_weight="1"/>
+            android:layout_width="fill_parent"
+            android:layout_height="0dp"
+            android:layout_weight="1" />
 </LinearLayout>
 
 
diff --git a/samples/SearchableDictionary/res/layout/result.xml b/samples/SearchableDictionary/res/layout/result.xml
new file mode 100644
index 0000000..a0bbd59
--- /dev/null
+++ b/samples/SearchableDictionary/res/layout/result.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<!-- Layout for list items in the search results.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:padding="5dp">
+    <TextView
+            android:id="@+id/word"
+            style="@android:style/TextAppearance.Large"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+    <TextView
+            android:id="@+id/definition"
+            style="@android:style/TextAppearance.Small"
+            android:singleLine="true"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content" />
+</LinearLayout>
diff --git a/samples/SearchableDictionary/res/layout/word.xml b/samples/SearchableDictionary/res/layout/word.xml
index 21a5ed4..8db8449 100644
--- a/samples/SearchableDictionary/res/layout/word.xml
+++ b/samples/SearchableDictionary/res/layout/word.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
 /*
-** Copyright 2009, The Android Open Source Project
+** Copyright 2010, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
@@ -16,24 +16,24 @@
 ** limitations under the License.
 */
 -->
+<!-- Layout for WordActivity.
+ -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:orientation="vertical"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent">
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:padding="5dp">
     <TextView
             android:id="@+id/word"
-            android:textAppearance="?android:attr/textAppearanceLarge"
-            android:textSize="30sp"
+            android:textSize="35dp"
+            android:textColor="?android:textColorPrimary"
             android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@string/search_instructions"
-            android:layout_marginLeft="10dip"
-            android:layout_marginTop="5dip" />
+            android:layout_height="wrap_content" />
     <TextView
             android:id="@+id/definition"
-            android:textAppearance="?android:attr/textAppearanceMedium"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:text="@string/search_instructions"
-            android:layout_marginLeft="10dip" />
+            android:textSize="18dp"
+            android:textColor="?android:textColorSecondary"
+            android:paddingTop="10dp"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
 </LinearLayout>
diff --git a/samples/SearchableDictionary/res/menu/options_menu.xml b/samples/SearchableDictionary/res/menu/options_menu.xml
new file mode 100644
index 0000000..2aa7cf2
--- /dev/null
+++ b/samples/SearchableDictionary/res/menu/options_menu.xml
@@ -0,0 +1,24 @@
+<!--
+/*
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<!-- Options Menu for SearchableActivity and WordActivity.
+ -->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/search"
+          android:title="@string/menu_search"
+          android:icon="@drawable/ic_menu_search" />
+</menu>
diff --git a/samples/SearchableDictionary/res/raw/definitions.txt b/samples/SearchableDictionary/res/raw/definitions.txt
index e4e1cbd..703a5c9 100644
--- a/samples/SearchableDictionary/res/raw/definitions.txt
+++ b/samples/SearchableDictionary/res/raw/definitions.txt
@@ -56,6 +56,7 @@
 analyst - n. someone who is skilled at analyzing data
 analyze - v. break down into components or essential features
 anchor - v. fix firmly and stably
+android - n. a robot designed to look and behave like a human being
 annual - j. occurring or payable every year
 anonymous - j. having no known name or identity or known source
 antichrist - n. the adversary of Christ (or Christianity) mentioned in the New Testament
diff --git a/samples/SearchableDictionary/res/values/strings.xml b/samples/SearchableDictionary/res/values/strings.xml
index a39ba75..569e1d9 100644
--- a/samples/SearchableDictionary/res/values/strings.xml
+++ b/samples/SearchableDictionary/res/values/strings.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
 /*
-** Copyright 2009, The Android Open Source Project
+** Copyright 2010, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
@@ -16,14 +16,17 @@
 ** limitations under the License.
 */
 -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+<resources>
 
     <!-- The name of the application. -->
     <string name="app_name">Searchable Dictionary</string>
 
-    <!-- The label for the search results for this app (see searchable.xml for more details). -->
+    <!-- The label for use as a searchable item -->
     <string name="search_label">Dictionary</string>
 
+    <!-- The hint text that appears in the search box. -->
+    <string name="search_hint">Search the dictionary</string>
+
     <!-- The menu entry that invokes search. -->
     <string name="menu_search">Search</string>
 
@@ -34,5 +37,12 @@
     <string name="search_instructions">Press the search key to look up a word</string>
 
     <!-- Shown above search results when we receive a search request. -->
-    <string name="search_results">Search results for \'<xliff:g id="string">%s</xliff:g>\': </string>
+    <plurals name="search_results">
+      <item quantity="one">%d result for \"%s\": </item>
+      <item quantity="other">%d results for \"%s\": </item>
+    </plurals>
+
+    <!-- Search failure message. -->
+    <string name="no_results">No results found for \"%s\"</string>
+
 </resources>
diff --git a/samples/SearchableDictionary/res/xml/searchable.xml b/samples/SearchableDictionary/res/xml/searchable.xml
index 1edb57c..1faa4e1 100644
--- a/samples/SearchableDictionary/res/xml/searchable.xml
+++ b/samples/SearchableDictionary/res/xml/searchable.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
 /*
-** Copyright 2009, The Android Open Source Project
+** Copyright 2010, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
@@ -17,24 +17,20 @@
 */
 -->
 
-<!-- The attributes below configure how the search results will work:
- - the 'label' points to a short description used when searching within the application if
-   'badge mode' is used as specified with android:searchMode="useLabelAsBadge" (which it is not for
-    this application).
- - the 'searchSettingsDescription' points to a string that will be displayed underneath the
-   name of this application in the search settings to describe what content will be searched.
- - 'includeInGlobalSearch' will include this app's search suggestions in Quick Search Box.
- - 'searchSuggestAuthority' specifies the authority matching the authority of the
-   "DictionaryProvider" specified in the manifest.  This means the DictionaryProvider will be
-   queried for search suggestions.
- - 'searchSuggestIntentAction' the default intent action used in the intent that is launched based
-   on a user cilcking on a search suggestion.  This saves us from manually having to fill in the
-   SUGGEST_COLUMN_INTENT_ACTION column for each suggestion returned by the provider.
+<!-- The attributes below configure the Android search box appearance
+     and the search suggestions settings.
+     See the Developer Guide for more information
+     http://developer.android.com/guide/topics/search/
  -->
 <searchable xmlns:android="http://schemas.android.com/apk/res/android"
         android:label="@string/search_label"
+        android:hint="@string/search_hint"
         android:searchSettingsDescription="@string/settings_description"
+        android:searchSuggestAuthority="com.example.android.searchabledict.DictionaryProvider"
+        android:searchSuggestIntentAction="android.intent.action.VIEW"
+        android:searchSuggestIntentData="content://com.example.android.searchabledict.DictionaryProvider/dictionary"
+        android:searchSuggestSelection=" ?"
+        android:searchSuggestThreshold="1"
         android:includeInGlobalSearch="true"
-        android:searchSuggestAuthority="dictionary"
-        android:searchSuggestIntentAction="android.intent.action.VIEW">
-</searchable>
+        >
+ </searchable>
diff --git a/samples/SearchableDictionary/src/com/example/android/searchabledict/Dictionary.java b/samples/SearchableDictionary/src/com/example/android/searchabledict/Dictionary.java
deleted file mode 100644
index 59e735b..0000000
--- a/samples/SearchableDictionary/src/com/example/android/searchabledict/Dictionary.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.example.android.searchabledict;
-
-import android.content.res.Resources;
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * Contains logic to load the word of words and definitions and find a list of matching words
- * given a query.  Everything is held in memory; this is not a robust way to serve lots of
- * words and is only for demo purposes.
- *
- * You may want to consider using an SQLite database. In practice, you'll want to make sure your
- * suggestion provider is as efficient as possible, as the system will be taxed while performing
- * searches across many sources for each keystroke the user enters into Quick Search Box.
- */
-public class Dictionary {
-
-    public static class Word {
-        public final String word;
-        public final String definition;
-
-        public Word(String word, String definition) {
-            this.word = word;
-            this.definition = definition;
-        }
-    }
-
-    private static final Dictionary sInstance = new Dictionary();
-
-    public static Dictionary getInstance() {
-        return sInstance;
-    }
-
-    private final Map<String, List<Word>> mDict = new ConcurrentHashMap<String, List<Word>>();
-
-    private Dictionary() {
-    }
-
-    private boolean mLoaded = false;
-
-    /**
-     * Loads the words and definitions if they haven't been loaded already.
-     *
-     * @param resources Used to load the file containing the words and definitions.
-     */
-    public synchronized void ensureLoaded(final Resources resources) {
-        if (mLoaded) return;
-
-        new Thread(new Runnable() {
-            public void run() {
-                try {
-                    loadWords(resources);
-                } catch (IOException e) {
-                    throw new RuntimeException(e);
-                }
-            }
-        }).start();
-    }
-
-    private synchronized void loadWords(Resources resources) throws IOException {
-        if (mLoaded) return;
-
-        Log.d("dict", "loading words");
-        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 < 2) continue;
-                addWord(strings[0].trim(), strings[1].trim());
-            }
-        } finally {
-            reader.close();
-        }
-        mLoaded = true;
-    }
-
-
-    public List<Word> getMatches(String query) {
-        List<Word> list = mDict.get(query);
-        return list == null ? Collections.EMPTY_LIST : list;
-    }
-
-    private void addWord(String word, String definition) {
-        final Word theWord = new Word(word, definition);
-
-        final int len = word.length();
-        for (int i = 0; i < len; i++) {
-            final String prefix = word.substring(0, len - i);
-            addMatch(prefix, theWord);
-        }
-    }
-
-    private void addMatch(String query, Word word) {
-        List<Word> matches = mDict.get(query);
-        if (matches == null) {
-            matches = new ArrayList<Word>();
-            mDict.put(query, matches);
-        }
-        matches.add(word);
-    }
-}
diff --git a/samples/SearchableDictionary/src/com/example/android/searchabledict/DictionaryDatabase.java b/samples/SearchableDictionary/src/com/example/android/searchabledict/DictionaryDatabase.java
new file mode 100644
index 0000000..0f854c3
--- /dev/null
+++ b/samples/SearchableDictionary/src/com/example/android/searchabledict/DictionaryDatabase.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.searchabledict;
+
+import android.app.SearchManager;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.provider.BaseColumns;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.HashMap;
+
+/**
+ * Contains logic to return specific words from the dictionary, and
+ * load the dictionary table when it needs to be created.
+ */
+public class DictionaryDatabase {
+    private static final String TAG = "DictionaryDatabase";
+
+    //The columns we'll include in the dictionary table
+    public static final String KEY_WORD = SearchManager.SUGGEST_COLUMN_TEXT_1;
+    public static final String KEY_DEFINITION = SearchManager.SUGGEST_COLUMN_TEXT_2;
+
+    private static final String DATABASE_NAME = "dictionary";
+    private static final String FTS_VIRTUAL_TABLE = "FTSdictionary";
+    private static final int DATABASE_VERSION = 2;
+
+    private final DictionaryOpenHelper mDatabaseOpenHelper;
+    private static final HashMap<String,String> mColumnMap = buildColumnMap();
+
+    /**
+     * Constructor
+     * @param context The Context within which to work, used to create the DB
+     */
+    public DictionaryDatabase(Context context) {
+        mDatabaseOpenHelper = new DictionaryOpenHelper(context);
+    }
+
+    /**
+     * Builds a map for all columns that may be requested, which will be given to the 
+     * SQLiteQueryBuilder. This is a good way to define aliases for column names, but must include 
+     * all columns, even if the value is the key. This allows the ContentProvider to request
+     * columns w/o the need to know real column names and create the alias itself.
+     */
+    private static HashMap<String,String> buildColumnMap() {
+        HashMap<String,String> map = new HashMap<String,String>();
+        map.put(KEY_WORD, KEY_WORD);
+        map.put(KEY_DEFINITION, KEY_DEFINITION);
+        map.put(BaseColumns._ID, "rowid AS " +
+                BaseColumns._ID);
+        map.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID, "rowid AS " +
+                SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID);
+        map.put(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID, "rowid AS " +
+                SearchManager.SUGGEST_COLUMN_SHORTCUT_ID);
+        return map;
+    }
+
+    /**
+     * Returns a Cursor positioned at the word specified by rowId
+     *
+     * @param rowId id of word to retrieve
+     * @param columns The columns to include, if null then all are included
+     * @return Cursor positioned to matching word, or null if not found.
+     */
+    public Cursor getWord(String rowId, String[] columns) {
+        String selection = "rowid = ?";
+        String[] selectionArgs = new String[] {rowId};
+
+        return query(selection, selectionArgs, columns);
+
+        /* This builds a query that looks like:
+         *     SELECT <columns> FROM <table> WHERE rowid = <rowId>
+         */
+    }
+
+    /**
+     * Returns a Cursor over all words that match the given query
+     *
+     * @param query The string to search for
+     * @param columns The columns to include, if null then all are included
+     * @return Cursor over all words that match, or null if none found.
+     */
+    public Cursor getWordMatches(String query, String[] columns) {
+        String selection = KEY_WORD + " MATCH ?";
+        String[] selectionArgs = new String[] {query+"*"};
+
+        return query(selection, selectionArgs, columns);
+
+        /* This builds a query that looks like:
+         *     SELECT <columns> FROM <table> WHERE <KEY_WORD> MATCH 'query*'
+         * which is an FTS3 search for the query text (plus a wildcard) inside the word column.
+         *
+         * - "rowid" is the unique id for all rows but we need this value for the "_id" column in
+         *    order for the Adapters to work, so the columns need to make "_id" an alias for "rowid"
+         * - "rowid" also needs to be used by the SUGGEST_COLUMN_INTENT_DATA alias in order
+         *   for suggestions to carry the proper intent data.
+         *   These aliases are defined in the DictionaryProvider when queries are made.
+         * - This can be revised to also search the definition text with FTS3 by changing
+         *   the selection clause to use FTS_VIRTUAL_TABLE instead of KEY_WORD (to search across
+         *   the entire table, but sorting the relevance could be difficult.
+         */
+    }
+
+    /**
+     * Performs a database query.
+     * @param selection The selection clause
+     * @param selectionArgs Selection arguments for "?" components in the selection
+     * @param columns The columns to return
+     * @return A Cursor over all rows matching the query
+     */
+    private Cursor query(String selection, String[] selectionArgs, String[] columns) {
+        /* The SQLiteBuilder provides a map for all possible columns requested to
+         * actual columns in the database, creating a simple column alias mechanism
+         * by which the ContentProvider does not need to know the real column names
+         */
+        SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
+        builder.setTables(FTS_VIRTUAL_TABLE);
+        builder.setProjectionMap(mColumnMap);
+
+        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;
+    }
+
+
+    /**
+     * This creates/opens the database.
+     */
+    private static class DictionaryOpenHelper extends SQLiteOpenHelper {
+
+        private final Context mHelperContext;
+        private SQLiteDatabase mDatabase;
+
+        /* Note that FTS3 does not support column constraints and thus, you cannot
+         * declare a primary key. However, "rowid" is automatically used as a unique
+         * identifier, so when making requests, we will use "_id" as an alias for "rowid"
+         */
+        private static final String FTS_TABLE_CREATE =
+                    "CREATE VIRTUAL TABLE " + FTS_VIRTUAL_TABLE +
+                    " USING fts3 (" +
+                    KEY_WORD + ", " +
+                    KEY_DEFINITION + ");";
+
+        DictionaryOpenHelper(Context context) {
+            super(context, DATABASE_NAME, null, DATABASE_VERSION);
+            mHelperContext = context;
+        }
+
+        @Override
+        public void onCreate(SQLiteDatabase db) {
+            mDatabase = db;
+            mDatabase.execSQL(FTS_TABLE_CREATE);
+            loadDictionary();
+        }
+
+        /**
+         * Starts a thread to load the database table with words
+         */
+        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 {
+            Log.d(TAG, "Loading words...");
+            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 < 2) continue;
+                    long id = addWord(strings[0].trim(), strings[1].trim());
+                    if (id < 0) {
+                        Log.e(TAG, "unable to add word: " + strings[0].trim());
+                    }
+                }
+            } finally {
+                reader.close();
+            }
+            Log.d(TAG, "DONE loading words.");
+        }
+
+        /**
+         * Add a word to the dictionary.
+         * @return rowId or -1 if failed
+         */
+        public long addWord(String word, String definition) {
+            ContentValues initialValues = new ContentValues();
+            initialValues.put(KEY_WORD, word);
+            initialValues.put(KEY_DEFINITION, definition);
+
+            return mDatabase.insert(FTS_VIRTUAL_TABLE, null, initialValues);
+        }
+
+        @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);
+        }
+    }
+
+}
diff --git a/samples/SearchableDictionary/src/com/example/android/searchabledict/DictionaryProvider.java b/samples/SearchableDictionary/src/com/example/android/searchabledict/DictionaryProvider.java
index 586fddb..c510e92 100644
--- a/samples/SearchableDictionary/src/com/example/android/searchabledict/DictionaryProvider.java
+++ b/samples/SearchableDictionary/src/com/example/android/searchabledict/DictionaryProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2010 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,144 +18,186 @@
 
 import android.app.SearchManager;
 import android.content.ContentProvider;
+import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.UriMatcher;
-import android.content.res.Resources;
 import android.database.Cursor;
-import android.database.MatrixCursor;
 import android.net.Uri;
-import android.text.TextUtils;
-
-import java.util.List;
+import android.provider.BaseColumns;
 
 /**
- * Provides search suggestions for a list of words and their definitions.
+ * Provides access to the dictionary database.
  */
 public class DictionaryProvider extends ContentProvider {
+    String TAG = "DictionaryProvider";
 
-    public static String AUTHORITY = "dictionary";
+    public static String AUTHORITY = "com.example.android.searchabledict.DictionaryProvider";
+    public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/dictionary");
 
-    private static final int SEARCH_SUGGEST = 0;
-    private static final int SHORTCUT_REFRESH = 1;
+    // MIME types used for searching words or looking up a single definition
+    public static final String WORDS_MIME_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE +
+                                                  "/vnd.example.android.searchabledict";
+    public static final String DEFINITION_MIME_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE +
+                                                       "/vnd.example.android.searchabledict";
+
+    private DictionaryDatabase mDictionary;
+
+    // UriMatcher stuff
+    private static final int SEARCH_WORDS = 0;
+    private static final int GET_WORD = 1;
+    private static final int SEARCH_SUGGEST = 2;
+    private static final int REFRESH_SHORTCUT = 3;
     private static final UriMatcher sURIMatcher = buildUriMatcher();
 
     /**
-     * The columns we'll include in our search suggestions.  There are others that could be used
-     * to further customize the suggestions, see the docs in {@link SearchManager} for the details
-     * on additional columns that are supported.
-     */
-    private static final String[] COLUMNS = {
-            "_id",  // must include this column
-            SearchManager.SUGGEST_COLUMN_TEXT_1,
-            SearchManager.SUGGEST_COLUMN_TEXT_2,
-            SearchManager.SUGGEST_COLUMN_INTENT_DATA,
-            };
-
-
-    /**
-     * Sets up a uri matcher for search suggestion and shortcut refresh queries.
+     * Builds up a UriMatcher for search suggestion and shortcut refresh queries.
      */
     private static UriMatcher buildUriMatcher() {
         UriMatcher matcher =  new UriMatcher(UriMatcher.NO_MATCH);
+        // to get definitions...
+        matcher.addURI(AUTHORITY, "dictionary", SEARCH_WORDS);
+        matcher.addURI(AUTHORITY, "dictionary/#", GET_WORD);
+        // to get suggestions...
         matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, SEARCH_SUGGEST);
         matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH_SUGGEST);
-        matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT, SHORTCUT_REFRESH);
-        matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*", SHORTCUT_REFRESH);
+
+        /* The following are unused in this implementation, but if we include
+         * {@link SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} as a column in our suggestions table, we
+         * could expect to receive refresh queries when a shortcutted suggestion is displayed in
+         * Quick Search Box, in which case, the following Uris would be provided and we
+         * would return a cursor with a single item representing the refreshed suggestion data.
+         */
+        matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT, REFRESH_SHORTCUT);
+        matcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*", REFRESH_SHORTCUT);
         return matcher;
     }
 
     @Override
     public boolean onCreate() {
-        Resources resources = getContext().getResources();
-        Dictionary.getInstance().ensureLoaded(resources);
+        mDictionary = new DictionaryDatabase(getContext());
         return true;
     }
 
+    /**
+     * Handles all the dictionary searches and suggestion queries from the Search Manager.
+     * When requesting a specific word, the uri alone is required.
+     * When searching all of the dictionary for matches, the selectionArgs argument must carry
+     * the search query as the first element.
+     * All other arguments are ignored.
+     */
     @Override
     public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
-            String sortOrder) {
-        if (!TextUtils.isEmpty(selection)) {
-            throw new IllegalArgumentException("selection not allowed for " + uri);
-        }
-        if (selectionArgs != null && selectionArgs.length != 0) {
-            throw new IllegalArgumentException("selectionArgs not allowed for " + uri);
-        }
-        if (!TextUtils.isEmpty(sortOrder)) {
-            throw new IllegalArgumentException("sortOrder not allowed for " + uri);
-        }
+                        String sortOrder) {
+
+        // Use the UriMatcher to see what kind of query we have and format the db query accordingly
         switch (sURIMatcher.match(uri)) {
             case SEARCH_SUGGEST:
-                String query = null;
-                if (uri.getPathSegments().size() > 1) {
-                    query = uri.getLastPathSegment().toLowerCase();
+                if (selectionArgs == null) {
+                  throw new IllegalArgumentException(
+                      "selectionArgs must be provided for the Uri: " + uri);
                 }
-                return getSuggestions(query, projection);
-            case SHORTCUT_REFRESH:
-                String shortcutId = null;
-                if (uri.getPathSegments().size() > 1) {
-                    shortcutId = uri.getLastPathSegment();
+                return getSuggestions(selectionArgs[0]);
+            case SEARCH_WORDS:
+                if (selectionArgs == null) {
+                  throw new IllegalArgumentException(
+                      "selectionArgs must be provided for the Uri: " + uri);
                 }
-                return refreshShortcut(shortcutId, projection);
+                return search(selectionArgs[0]);
+            case GET_WORD:
+                return getWord(uri);
+            case REFRESH_SHORTCUT:
+                return refreshShortcut(uri);
             default:
-                throw new IllegalArgumentException("Unknown URL " + uri);
+                throw new IllegalArgumentException("Unknown Uri: " + uri);
         }
     }
 
-    private Cursor getSuggestions(String query, String[] projection) {
-        String processedQuery = query == null ? "" : query.toLowerCase();
-        List<Dictionary.Word> words = Dictionary.getInstance().getMatches(processedQuery);
+    private Cursor getSuggestions(String query) {
+      query = query.toLowerCase();
+      String[] columns = new String[] {
+          BaseColumns._ID,
+          DictionaryDatabase.KEY_WORD,
+          DictionaryDatabase.KEY_DEFINITION,
+       /* SearchManager.SUGGEST_COLUMN_SHORTCUT_ID,
+                        (only if you want to refresh shortcuts) */
+          SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID};
 
-        MatrixCursor cursor = new MatrixCursor(COLUMNS);
-        long id = 0;
-        for (Dictionary.Word word : words) {
-            cursor.addRow(columnValuesOfWord(id++, word));
-        }
-
-        return cursor;
+      return mDictionary.getWordMatches(query, columns);
     }
 
-    private Object[] columnValuesOfWord(long id, Dictionary.Word word) {
-        return new Object[] {
-                id,                  // _id
-                word.word,           // text1
-                word.definition,     // text2
-                word.word,           // intent_data (included when clicking on item)
-        };
+    private Cursor search(String query) {
+      query = query.toLowerCase();
+      String[] columns = new String[] {
+          BaseColumns._ID,
+          DictionaryDatabase.KEY_WORD,
+          DictionaryDatabase.KEY_DEFINITION};
+
+      return mDictionary.getWordMatches(query, columns);
+    }
+
+    private Cursor getWord(Uri uri) {
+      String rowId = uri.getLastPathSegment();
+      String[] columns = new String[] {
+          DictionaryDatabase.KEY_WORD,
+          DictionaryDatabase.KEY_DEFINITION};
+
+      return mDictionary.getWord(rowId, columns);
+    }
+
+    private Cursor refreshShortcut(Uri uri) {
+      /* This won't be called with the current implementation, but if we include
+       * {@link SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} as a column in our suggestions table, we
+       * could expect to receive refresh queries when a shortcutted suggestion is displayed in
+       * Quick Search Box. In which case, this method will query the table for the specific
+       * word, using the given item Uri and provide all the columns originally provided with the
+       * suggestion query.
+       */
+      String rowId = uri.getLastPathSegment();
+      String[] columns = new String[] {
+          BaseColumns._ID,
+          DictionaryDatabase.KEY_WORD,
+          DictionaryDatabase.KEY_DEFINITION,
+          SearchManager.SUGGEST_COLUMN_SHORTCUT_ID,
+          SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID};
+
+      return mDictionary.getWord(rowId, columns);
     }
 
     /**
-     * Note: this is unused as is, but if we included
-     * {@link SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} as a column in our results, we
-     * could expect to receive refresh queries on this uri for the id provided, in which case we
-     * would return a cursor with a single item representing the refreshed suggestion data.
+     * This method is required in order to query the supported types.
+     * It's also useful in our own query() method to determine the type of Uri received.
      */
-    private Cursor refreshShortcut(String shortcutId, String[] projection) {
-        return null;
-    }
-
-    /**
-     * All queries for this provider are for the search suggestion and shortcut refresh mime type.
-     */
+    @Override
     public String getType(Uri uri) {
         switch (sURIMatcher.match(uri)) {
+            case SEARCH_WORDS:
+                return WORDS_MIME_TYPE;
+            case GET_WORD:
+                return DEFINITION_MIME_TYPE;
             case SEARCH_SUGGEST:
                 return SearchManager.SUGGEST_MIME_TYPE;
-            case SHORTCUT_REFRESH:
+            case REFRESH_SHORTCUT:
                 return SearchManager.SHORTCUT_MIME_TYPE;
             default:
                 throw new IllegalArgumentException("Unknown URL " + uri);
         }
     }
 
+    // Other required implementations...
+
+    @Override
     public Uri insert(Uri uri, ContentValues values) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public int delete(Uri uri, String selection, String[] selectionArgs) {
         throw new UnsupportedOperationException();
     }
 
+    @Override
     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
         throw new UnsupportedOperationException();
     }
+
 }
diff --git a/samples/SearchableDictionary/src/com/example/android/searchabledict/SearchableDictionary.java b/samples/SearchableDictionary/src/com/example/android/searchabledict/SearchableDictionary.java
index 4d27470..ef938f8 100644
--- a/samples/SearchableDictionary/src/com/example/android/searchabledict/SearchableDictionary.java
+++ b/samples/SearchableDictionary/src/com/example/android/searchabledict/SearchableDictionary.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2010 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,138 +18,114 @@
 
 import android.app.Activity;
 import android.app.SearchManager;
-import android.content.Context;
 import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
 import android.os.Bundle;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
 import android.view.Menu;
+import android.view.MenuInflater;
 import android.view.MenuItem;
+import android.view.View;
 import android.widget.AdapterView;
-import android.widget.BaseAdapter;
 import android.widget.ListView;
+import android.widget.SimpleCursorAdapter;
 import android.widget.TextView;
-import android.widget.TwoLineListItem;
-
-import java.util.List;
+import android.widget.AdapterView.OnItemClickListener;
 
 /**
- * The main activity for the dictionary.  Also displays search results triggered by the search
- * dialog.
+ * The main activity for the dictionary.
+ * Displays search results triggered by the search dialog and handles
+ * actions from search suggestions.
  */
 public class SearchableDictionary extends Activity {
 
-    private static final int MENU_SEARCH = 1;
-
     private TextView mTextView;
-    private ListView mList;
+    private ListView mListView;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+
+        mTextView = (TextView) findViewById(R.id.text);
+        mListView = (ListView) findViewById(R.id.list);
 
         Intent intent = getIntent();
 
-        setContentView(R.layout.main);
-        mTextView = (TextView) findViewById(R.id.textField);
-        mList = (ListView) findViewById(R.id.list);
-
         if (Intent.ACTION_VIEW.equals(intent.getAction())) {
-            // from click on search results
-            Dictionary.getInstance().ensureLoaded(getResources());
-            String word = intent.getDataString();
-            Dictionary.Word theWord = Dictionary.getInstance().getMatches(word).get(0);
-            launchWord(theWord);
+            // handles a click on a search suggestion; launches activity to show word
+            Intent wordIntent = new Intent(this, WordActivity.class);
+            wordIntent.setData(intent.getData());
+            startActivity(wordIntent);
             finish();
         } else if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
+            // handles a search query
             String query = intent.getStringExtra(SearchManager.QUERY);
-            mTextView.setText(getString(R.string.search_results, query));
-            WordAdapter wordAdapter = new WordAdapter(Dictionary.getInstance().getMatches(query));
-            mList.setAdapter(wordAdapter);
-            mList.setOnItemClickListener(wordAdapter);
+            showResults(query);
         }
+    }
 
-        Log.d("dict", intent.toString());
-        if (intent.getExtras() != null) {
-            Log.d("dict", intent.getExtras().keySet().toString());
+    /**
+     * Searches the dictionary and displays results for the given query.
+     * @param query The search query
+     */
+    private void showResults(String query) {
+
+        Cursor cursor = managedQuery(DictionaryProvider.CONTENT_URI, null, null,
+                                new String[] {query}, null);
+
+        if (cursor == null) {
+            // There are no results
+            mTextView.setText(getString(R.string.no_results, new Object[] {query}));
+        } else {
+            // Display the number of results
+            int count = cursor.getCount();
+            String countString = getResources().getQuantityString(R.plurals.search_results,
+                                    count, new Object[] {count, query});
+            mTextView.setText(countString);
+
+            // Specify the columns we want to display in the result
+            String[] from = new String[] { DictionaryDatabase.KEY_WORD,
+                                           DictionaryDatabase.KEY_DEFINITION };
+
+            // Specify the corresponding layout elements where we want the columns to go
+            int[] to = new int[] { R.id.word,
+                                   R.id.definition };
+
+            // Create a simple cursor adapter for the definitions and apply them to the ListView
+            SimpleCursorAdapter words = new SimpleCursorAdapter(this,
+                                          R.layout.result, cursor, from, to);
+            mListView.setAdapter(words);
+
+            // Define the on-click listener for the list items
+            mListView.setOnItemClickListener(new OnItemClickListener() {
+                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                    // Build the Intent used to open WordActivity with a specific word Uri
+                    Intent wordIntent = new Intent(getApplicationContext(), WordActivity.class);
+                    Uri data = Uri.withAppendedPath(DictionaryProvider.CONTENT_URI,
+                                                    String.valueOf(id));
+                    wordIntent.setData(data);
+                    startActivity(wordIntent);
+                }
+            });
         }
     }
 
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
-        menu.add(0, MENU_SEARCH, 0, R.string.menu_search)
-                .setIcon(android.R.drawable.ic_search_category_default)
-                .setAlphabeticShortcut(SearchManager.MENU_KEY);
-
-        return super.onCreateOptionsMenu(menu);
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.options_menu, menu);
+        return true;
     }
 
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         switch (item.getItemId()) {
-            case MENU_SEARCH:
+            case R.id.search:
                 onSearchRequested();
                 return true;
-        }
-        return super.onOptionsItemSelected(item);
-    }
-
-    private void launchWord(Dictionary.Word theWord) {
-        Intent next = new Intent();
-        next.setClass(this, WordActivity.class);
-        next.putExtra("word", theWord.word);
-        next.putExtra("definition", theWord.definition);
-        startActivity(next);
-    }
-
-    class WordAdapter extends BaseAdapter implements AdapterView.OnItemClickListener {
-
-        private final List<Dictionary.Word> mWords;
-        private final LayoutInflater mInflater;
-
-        public WordAdapter(List<Dictionary.Word> words) {
-            mWords = words;
-            mInflater = (LayoutInflater) SearchableDictionary.this.getSystemService(
-                    Context.LAYOUT_INFLATER_SERVICE);
-        }
-
-        public int getCount() {
-            return mWords.size();
-        }
-
-        public Object getItem(int position) {
-            return position;
-        }
-
-        public long getItemId(int position) {
-            return position;
-        }
-
-        public View getView(int position, View convertView, ViewGroup parent) {
-            TwoLineListItem view = (convertView != null) ? (TwoLineListItem) convertView :
-                    createView(parent);
-            bindView(view, mWords.get(position));
-            return view;
-        }
-
-        private TwoLineListItem createView(ViewGroup parent) {
-            TwoLineListItem item = (TwoLineListItem) mInflater.inflate(
-                    android.R.layout.simple_list_item_2, parent, false);
-            item.getText2().setSingleLine();
-            item.getText2().setEllipsize(TextUtils.TruncateAt.END);
-            return item;
-        }
-
-        private void bindView(TwoLineListItem view, Dictionary.Word word) {
-            view.getText1().setText(word.word);
-            view.getText2().setText(word.definition);
-        }
-
-        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-            launchWord(mWords.get(position));
+            default:
+                return false;
         }
     }
 }
diff --git a/samples/SearchableDictionary/src/com/example/android/searchabledict/WordActivity.java b/samples/SearchableDictionary/src/com/example/android/searchabledict/WordActivity.java
index 1c4b8b4..00dc270 100644
--- a/samples/SearchableDictionary/src/com/example/android/searchabledict/WordActivity.java
+++ b/samples/SearchableDictionary/src/com/example/android/searchabledict/WordActivity.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2010 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -17,33 +17,58 @@
 package com.example.android.searchabledict;
 
 import android.app.Activity;
+import android.database.Cursor;
+import android.net.Uri;
 import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
 import android.widget.TextView;
-import android.content.Intent;
 
 /**
  * Displays a word and its definition.
  */
 public class WordActivity extends Activity {
 
-    private TextView mWord;
-    private TextView mDefinition;
-
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-
         setContentView(R.layout.word);
 
-        mWord = (TextView) findViewById(R.id.word);
-        mDefinition = (TextView) findViewById(R.id.definition);
+        Uri uri = getIntent().getData();
+        Cursor cursor = managedQuery(uri, null, null, null, null);
 
-        Intent intent = getIntent();
+        if (cursor == null) {
+            finish();
+        } else {
+            cursor.moveToFirst();
 
-        String word = intent.getStringExtra("word");
-        String definition = intent.getStringExtra("definition");
+            TextView word = (TextView) findViewById(R.id.word);
+            TextView definition = (TextView) findViewById(R.id.definition);
 
-        mWord.setText(word);
-        mDefinition.setText(definition);
+            int wIndex = cursor.getColumnIndexOrThrow(DictionaryDatabase.KEY_WORD);
+            int dIndex = cursor.getColumnIndexOrThrow(DictionaryDatabase.KEY_DEFINITION);
+
+            word.setText(cursor.getString(wIndex));
+            definition.setText(cursor.getString(dIndex));
+        }
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.options_menu, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.search:
+                onSearchRequested();
+                return true;
+            default:
+                return false;
+        }
     }
 }