am d71605b0: am 8c63b97f: Rename framework-common testdef to android-common.
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;
+        }
     }
 }
diff --git a/samples/XmlAdapters/Android.mk b/samples/XmlAdapters/Android.mk
new file mode 100644
index 0000000..24a3327
--- /dev/null
+++ b/samples/XmlAdapters/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := samples
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := XmlAdaptersSample
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_PACKAGE)
+
+# Use the following include to make our test apk.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/samples/XmlAdapters/AndroidManifest.xml b/samples/XmlAdapters/AndroidManifest.xml
new file mode 100644
index 0000000..af040ad
--- /dev/null
+++ b/samples/XmlAdapters/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.xmladapters">
+    
+    <uses-permission android:name="android.permission.READ_CONTACTS" />
+
+    <application android:label="@string/app_name">
+        <activity android:name="ContactsListActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>
diff --git a/samples/XmlAdapters/res/drawable-hdpi/ic_contact_picture.png b/samples/XmlAdapters/res/drawable-hdpi/ic_contact_picture.png
new file mode 100644
index 0000000..a60565a
--- /dev/null
+++ b/samples/XmlAdapters/res/drawable-hdpi/ic_contact_picture.png
Binary files differ
diff --git a/samples/XmlAdapters/res/layout/contact_item.xml b/samples/XmlAdapters/res/layout/contact_item.xml
new file mode 100644
index 0000000..6fcb109
--- /dev/null
+++ b/samples/XmlAdapters/res/layout/contact_item.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_vertical"
+    android:minHeight="?android:attr/listPreferredItemHeight">
+
+    <TextView
+        android:id="@+id/name"
+        android:layout_width="0px"
+        android:layout_weight="1.0"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceLarge"
+        android:gravity="center_vertical"
+        android:drawablePadding="6dip"
+        android:paddingLeft="6dip"
+        android:paddingRight="6dip" />
+
+    <ImageView
+        android:id="@+id/star"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />    
+
+</LinearLayout>
diff --git a/samples/XmlAdapters/res/layout/contacts_list.xml b/samples/XmlAdapters/res/layout/contacts_list.xml
new file mode 100644
index 0000000..973fe4b
--- /dev/null
+++ b/samples/XmlAdapters/res/layout/contacts_list.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+ 
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <ListView
+        android:id="@android:id/list"
+        android:adapter="@xml/contacts"
+        android:layout_width="match_parent" 
+        android:layout_height="match_parent" />
+
+    <TextView android:id="@android:id/empty"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        android:text="@string/no_contacts"
+        android:visibility="gone" />
+
+</merge>    
diff --git a/samples/XmlAdapters/res/values/strings.xml b/samples/XmlAdapters/res/values/strings.xml
new file mode 100644
index 0000000..3a0b5fe
--- /dev/null
+++ b/samples/XmlAdapters/res/values/strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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" BASI
+     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.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name">Xml Contacts Adapter</string>
+    <string name="no_contacts">No contacts available</string>
+</resources>
diff --git a/samples/XmlAdapters/res/xml/contacts.xml b/samples/XmlAdapters/res/xml/contacts.xml
new file mode 100644
index 0000000..b33d948
--- /dev/null
+++ b/samples/XmlAdapters/res/xml/contacts.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<cursor-adapter xmlns:android="http://schemas.android.com/apk/res/android"
+	android:uri="content://com.android.contacts/contacts"
+    android:selection="has_phone_number=1"
+	android:layout="@layout/contact_item">
+
+	<bind android:from="display_name" android:to="@id/name" android:as="string" />
+    <bind android:from="starred" android:to="@id/star" android:as="drawable">
+        <map android:fromValue="0" android:toValue="@android:drawable/star_big_off" />
+        <map android:fromValue="1" android:toValue="@android:drawable/star_big_on" />
+    </bind>
+    <bind android:from="_id" android:to="@id/name"
+          android:as="com.example.android.xmladapters.ContactPhotoBinder" />
+
+</cursor-adapter>
diff --git a/samples/XmlAdapters/src/com/example/android/xmladapters/ContactPhotoBinder.java b/samples/XmlAdapters/src/com/example/android/xmladapters/ContactPhotoBinder.java
new file mode 100644
index 0000000..947eb2a
--- /dev/null
+++ b/samples/XmlAdapters/src/com/example/android/xmladapters/ContactPhotoBinder.java
@@ -0,0 +1,96 @@
+/*
+ * 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.xmladapters;
+
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.view.View;
+import android.widget.Adapters;
+import android.widget.TextView;
+
+import java.io.InputStream;
+import java.util.HashMap;
+
+/**
+ * This custom cursor binder is used by the adapter defined in res/xml to
+ * bin contacts photos to their respective list item. This binder simply
+ * queries a contact's photo based on the contact's id and sets the
+ * photo as a compound drawable on the TextView used to display the contact's
+ * name.
+ */
+public class ContactPhotoBinder extends Adapters.CursorBinder {
+    private static final int PHOTO_SIZE_DIP = 54;
+    
+    private final Drawable mDefault;
+    private final HashMap<Long, Drawable> mCache;
+    private final Resources mResources;
+    private final int mPhotoSize;
+
+    public ContactPhotoBinder(Context context, Adapters.CursorTransformation transformation) {
+        super(context, transformation);
+
+        mResources = mContext.getResources();
+        // Default picture used when a contact does not provide one
+        mDefault = mResources.getDrawable(R.drawable.ic_contact_picture);
+        // Cache used to avoid requerying contacts photos every time
+        mCache = new HashMap<Long, Drawable>();
+        // Compute the size of the photo based on the display's density
+        mPhotoSize = (int) (PHOTO_SIZE_DIP * mResources.getDisplayMetrics().density + 0.5f);
+    }
+
+    @Override
+    public boolean bind(View view, Cursor cursor, int columnIndex) {
+        final long id = cursor.getLong(columnIndex);
+        
+        // First check whether we have already cached the contact's photo
+        Drawable d = mCache.get(id);
+        
+        if (d == null) {
+            // If the photo wasn't in the cache, ask the contacts provider for
+            // an input stream we can use to load the photo
+            Uri uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, id);
+            InputStream stream = ContactsContract.Contacts.openContactPhotoInputStream(
+                    mContext.getContentResolver(), uri);
+    
+            // Creates the drawable for the contact's photo or use our fallback drawable
+            if (stream != null) {
+                Bitmap bitmap = BitmapFactory.decodeStream(stream);
+                d = new BitmapDrawable(mResources, bitmap);
+            } else {
+                d = mDefault;
+            }
+
+            d.setBounds(0, 0, mPhotoSize, mPhotoSize);
+            ((TextView) view).setCompoundDrawables(d, null, null, null);
+
+            // Remember the photo associated with this contact
+            mCache.put(id, d);
+        } else {
+            ((TextView) view).setCompoundDrawables(d, null, null, null);
+        }
+
+        return true;
+    }
+}
diff --git a/samples/XmlAdapters/src/com/example/android/xmladapters/ContactsListActivity.java b/samples/XmlAdapters/src/com/example/android/xmladapters/ContactsListActivity.java
new file mode 100644
index 0000000..bf5ab58
--- /dev/null
+++ b/samples/XmlAdapters/src/com/example/android/xmladapters/ContactsListActivity.java
@@ -0,0 +1,35 @@
+/*
+ * 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.xmladapters;
+
+import android.app.ListActivity;
+import android.os.Bundle;
+
+/**
+ * This activity demonstrates how to create a complex UI using a ListView
+ * and an adapter defined in XML.
+ * 
+ * The following activity shows a list of contacts, their starred status
+ * and their photos, using the adapter defined in res/xml.
+ */
+public class ContactsListActivity extends ListActivity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.contacts_list);
+    }
+}
diff --git a/simulator/app/MessageStream.cpp b/simulator/app/MessageStream.cpp
index 2397c63..c52e7c4 100644
--- a/simulator/app/MessageStream.cpp
+++ b/simulator/app/MessageStream.cpp
@@ -8,6 +8,7 @@
 
 #include "utils/Log.h"
 
+#include <stdint.h>
 #include <string.h>
 #include <assert.h>
 
@@ -338,7 +339,7 @@
      * and capability flags.
      */
     if (initiateHello) {
-        long data = kHelloMsg;
+        int32_t data = kHelloMsg;
         Message msg;
 
         /* send hello */
@@ -357,14 +358,15 @@
             return false;
         }
 
-        const long* pAck;
-        pAck = (const long*) msg.getData();
+        const int32_t* pAck;
+        pAck = (const int32_t*) msg.getData();
         if (pAck == NULL || *pAck != kHelloAckMsg) {
-            LOG(LOG_WARN, "", "hello ack was bad\n");
+            LOG(LOG_WARN, "", "hello ack was bad (%08x vs %08x)\n",
+                *pAck, kHelloAckMsg);
             return false;
         }
     } else {
-        long data = kHelloAckMsg;
+        int32_t data = kHelloAckMsg;
         Message msg;
 
         LOG(LOG_DEBUG, "", "waiting for hello from peer\n");
@@ -375,8 +377,8 @@
             return false;
         }
 
-        const long* pAck;
-        pAck = (const long*) msg.getData();
+        const int32_t* pAck;
+        pAck = (const int32_t*) msg.getData();
         if (pAck == NULL || *pAck != kHelloMsg) {
             LOG(LOG_WARN, "", "hello was bad\n");
             return false;
diff --git a/testrunner/test_defs.xml b/testrunner/test_defs.xml
index 4cc91c8..8c6a0fe 100644
--- a/testrunner/test_defs.xml
+++ b/testrunner/test_defs.xml
@@ -380,7 +380,8 @@
 <test name="browser"
     build_path="packages/apps/Browser"
     package="com.android.browser.tests"
-    coverage_target="Browser" />
+    coverage_target="Browser"
+    continuous="true" />
 
 <test name="calendar"
     build_path="packages/apps/Calendar"