Next pass over the contact search UI.

- Search is now a separate activity
- Search bar has animations to make it slide in from the top
- New search plate resources
- Showing a magnifying glass on the right hand side of the search field
- Showing pictures and QuickContact in search result UI
- Search UI now start with no results instead of all visible contacts
- Translucent background for search UI when no filter specified
- Search can now be launched from most activities in the app
- Allowing search in pickers

Bug: 2447965
Change-Id: Ie35ce6df5e850d9c9380a25ed3970e114e9d6929
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index e924fd8..7cac590 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -135,10 +135,6 @@
                 <data android:mimeType="vnd.android.cursor.dir/contact" android:host="com.android.contacts" />
             </intent-filter>
 
-            <intent-filter>
-                <action android:name="com.android.contacts.action.FILTER_CONTACTS" />
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
         </activity-alias>
 
         <!-- An empty activity that presents the DialtactActivity's Favorites tab -->
@@ -249,6 +245,17 @@
             </intent-filter>
         </activity>
 
+        <!-- The contacts search/filter UI -->
+        <activity android:name="ContactsListActivity$ContactsSearchActivity"
+            android:theme="@style/ContactsSearchTheme"
+            android:windowSoftInputMode="stateAlwaysVisible|adjustPan"
+        >
+            <intent-filter>
+                <action android:name="com.android.contacts.action.FILTER_CONTACTS" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:mimeType="vnd.android.cursor.dir/contact" android:host="com.android.contacts" />
+            </intent-filter>
+        </activity>
 
         <!-- Used to select display and sync groups -->
         <activity android:name=".ui.ContactsPreferencesActivity" android:label="@string/displayGroups" />
diff --git a/res/anim/search_bar_enter.xml b/res/anim/search_bar_enter.xml
new file mode 100644
index 0000000..f0ee241
--- /dev/null
+++ b/res/anim/search_bar_enter.xml
@@ -0,0 +1,26 @@
+<?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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    android:interpolator="@android:anim/decelerate_interpolator">
+	<translate android:fromYDelta="-32" android:toYDelta="0"
+            android:duration="@android:integer/config_shortAnimTime"/>
+	<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+            android:duration="@android:integer/config_shortAnimTime" />
+</set>
diff --git a/res/anim/search_bar_exit.xml b/res/anim/search_bar_exit.xml
new file mode 100644
index 0000000..c0eff2b
--- /dev/null
+++ b/res/anim/search_bar_exit.xml
@@ -0,0 +1,26 @@
+<?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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    android:interpolator="@android:anim/accelerate_interpolator">
+	<translate android:fromYDelta="0" android:toYDelta="-32"
+            android:duration="@android:integer/config_shortAnimTime"/>
+	<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+            android:duration="@android:integer/config_shortAnimTime" />
+</set>
diff --git a/res/drawable-hdpi-finger/bg_blk_search_contact.9.png b/res/drawable-hdpi-finger/bg_blk_search_contact.9.png
index 7b66b3b..db83477 100644
--- a/res/drawable-hdpi-finger/bg_blk_search_contact.9.png
+++ b/res/drawable-hdpi-finger/bg_blk_search_contact.9.png
Binary files differ
diff --git a/res/drawable-hdpi/magnifying_glass.png b/res/drawable-hdpi/magnifying_glass.png
new file mode 100755
index 0000000..ac88fb4
--- /dev/null
+++ b/res/drawable-hdpi/magnifying_glass.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/bg_blk_search_contact.9.png b/res/drawable-mdpi-finger/bg_blk_search_contact.9.png
index e04234c..2694b5c 100644
--- a/res/drawable-mdpi-finger/bg_blk_search_contact.9.png
+++ b/res/drawable-mdpi-finger/bg_blk_search_contact.9.png
Binary files differ
diff --git a/res/drawable-mdpi/magnifying_glass.png b/res/drawable-mdpi/magnifying_glass.png
new file mode 100755
index 0000000..2592ae0
--- /dev/null
+++ b/res/drawable-mdpi/magnifying_glass.png
Binary files differ
diff --git a/res/layout-finger/contacts_list_content.xml b/res/layout-finger/contacts_list_content.xml
index e2ade92..2474975 100644
--- a/res/layout-finger/contacts_list_content.xml
+++ b/res/layout-finger/contacts_list_content.xml
@@ -19,11 +19,8 @@
         android:id="@+id/pinned_header_list_layout"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:orientation="vertical">
-
-    <include android:id="@+id/searchView" 
-        layout="@layout/search_bar"
-        android:visibility="gone"/>
+        android:orientation="vertical"
+        >
 
     <view
         class="com.android.contacts.PinnedHeaderListView" 
diff --git a/res/layout-finger/contacts_list_content_join.xml b/res/layout-finger/contacts_list_content_join.xml
index f4704cd..b50713b 100644
--- a/res/layout-finger/contacts_list_content_join.xml
+++ b/res/layout-finger/contacts_list_content_join.xml
@@ -20,10 +20,6 @@
         android:layout_height="match_parent"
         android:orientation="vertical">
 
-    <include android:id="@+id/searchView" 
-        layout="@layout/search_bar"
-        android:visibility="gone"/>
-
     <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content" 
diff --git a/res/layout-finger/contacts_list_search_all_item.xml b/res/layout-finger/contacts_list_search_all_item.xml
new file mode 100644
index 0000000..da2a3d2
--- /dev/null
+++ b/res/layout-finger/contacts_list_search_all_item.xml
@@ -0,0 +1,39 @@
+<?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.
+ */
+-->
+
+<LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+>
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="?android:attr/listPreferredItemHeight"
+        android:gravity="center_vertical|left"
+        android:textAppearance="?android:attr/textAppearanceLarge"
+        android:text="@string/search_for_all_contacts"
+        android:paddingLeft="14dip"
+    />
+
+    <View android:id="@+id/list_divider"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="@*android:drawable/divider_horizontal_dark_opaque"
+    />
+</LinearLayout>
diff --git a/res/layout-finger/contacts_search_content.xml b/res/layout-finger/contacts_search_content.xml
new file mode 100644
index 0000000..ae72376
--- /dev/null
+++ b/res/layout-finger/contacts_search_content.xml
@@ -0,0 +1,42 @@
+<?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:id="@+id/pinned_header_list_layout"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        android:background="@color/translucent_search_background"
+        >
+
+    <include android:id="@+id/searchView"
+        layout="@layout/search_bar"/>
+
+    <ListView
+        android:id="@android:id/list"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:fastScrollEnabled="true"
+        android:background="@android:color/background_dark"
+    />
+
+    <!-- Transparent filler -->
+    <View android:id="@android:id/empty"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+    />
+</LinearLayout>
diff --git a/res/layout-finger/search_bar.xml b/res/layout-finger/search_bar.xml
index 155c2ac..d322948 100644
--- a/res/layout-finger/search_bar.xml
+++ b/res/layout-finger/search_bar.xml
@@ -31,7 +31,7 @@
         android:layout_height="wrap_content"
         android:orientation="vertical"
         android:paddingLeft="4dip"
-        android:paddingRight="4dip"
+        android:paddingRight="10dip"
         android:paddingTop="6dip"
         android:paddingBottom="0dip"        
         >
@@ -62,20 +62,11 @@
                 android:singleLine="true"
                 android:ellipsize="end"
                 android:inputType="textNoSuggestions"
-                android:imeOptions="actionGo|flagNoExtractUi"
+                android:imeOptions="flagNoExtractUi"
                 android:hint="@string/search_bar_hint"
+                android:drawableRight="@drawable/magnifying_glass"
                 android:freezesText="true"
             />
-
-            <ImageButton
-                android:id="@+id/search_btn"
-                android:layout_width="wrap_content"
-                android:layout_height="match_parent"
-                android:background="@drawable/btn_search_dialog"
-                android:src="@drawable/ic_btn_search"
-                android:layout_marginLeft="4dip"
-                android:layout_marginBottom="4dip"               
-            />
         </LinearLayout>
         
     </LinearLayout>
diff --git a/res/layout-finger/total_contacts.xml b/res/layout-finger/total_contacts.xml
index 54a804b..1cac674 100644
--- a/res/layout-finger/total_contacts.xml
+++ b/res/layout-finger/total_contacts.xml
@@ -34,17 +34,4 @@
         android:gravity="center_vertical"
     />
 
-    <TextView 
-        android:id="@+id/searchForMoreText"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:textColor="#ffbfbfbf"
-        android:textSize="14sp"
-        android:textStyle="normal"
-        android:layout_alignParentRight="true"
-        android:gravity="center_vertical"
-        android:text="@string/search_more_hint"
-        android:visibility="gone"
-    />
-
 </RelativeLayout>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 32d07c5..5f39532 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -24,4 +24,6 @@
     <color name="edit_divider">#ff666666</color>
     <color name="background_secondary">#ff202020</color>
     <color name="pinned_header_background">#ff202020</color>
+
+    <color name="translucent_search_background">#cc000000</color>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index d6388e5..30e7b11 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1326,6 +1326,7 @@
     <!-- Gray hint displayed in the search field in Contacts when empty -->
     <string name="search_bar_hint">Search contacts</string>
     
-    <!-- Hint displayed underneath the search button in Contacts-->
-    <string name="search_more_hint">Search to find more</string>
+    <!-- Button displayed underneath the list of filtered visible contacts -->
+    <string name="search_for_all_contacts">Search for all contacts</string>
+
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 0a6fe2f..ad4f4f6 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -20,6 +20,10 @@
         <item name="android:windowContentOverlay">@null</item>
     </style>
 
+    <style name="ContactsSearchTheme" parent="@android:Theme.Translucent.NoTitleBar">
+        <item name="android:windowAnimationStyle">@style/ContactsSearchAnimation</item>
+    </style>
+
     <style name="MinusButton">
         <item name="android:background">@drawable/btn_circle</item>
         <item name="android:src">@drawable/ic_btn_round_minus</item>
@@ -88,8 +92,12 @@
         <item name="android:windowExitAnimation">@anim/quickcontact_below_exit</item>
     </style>
 
+    <style name="ContactsSearchAnimation">
+        <item name="android:windowEnterAnimation">@anim/search_bar_enter</item>
+        <item name="android:windowExitAnimation">@anim/search_bar_exit</item>
+    </style>
+
     <style name="DummyAnimation">
         <item name="android:windowExitAnimation">@anim/dummy_animation</item>
     </style>
-
 </resources>
diff --git a/src/com/android/contacts/CallDetailActivity.java b/src/com/android/contacts/CallDetailActivity.java
index b2baa2c..3228692 100644
--- a/src/com/android/contacts/CallDetailActivity.java
+++ b/src/com/android/contacts/CallDetailActivity.java
@@ -365,4 +365,14 @@
             }
         }
     }
+
+    @Override
+    public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
+            boolean globalSearch) {
+        if (globalSearch) {
+            super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
+        } else {
+            ContactsSearchManager.startSearch(this, initialQuery);
+        }
+    }
 }
diff --git a/src/com/android/contacts/ContactOptionsActivity.java b/src/com/android/contacts/ContactOptionsActivity.java
index 38090aa..2c6142a 100644
--- a/src/com/android/contacts/ContactOptionsActivity.java
+++ b/src/com/android/contacts/ContactOptionsActivity.java
@@ -198,6 +198,16 @@
         values.put(Contacts.SEND_TO_VOICEMAIL, mSendToVoicemail);
         getContentResolver().update(mLookupUri, values, null, null);
     }
+
+    @Override
+    public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
+            boolean globalSearch) {
+        if (globalSearch) {
+            super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
+        } else {
+            ContactsSearchManager.startSearch(this, initialQuery);
+        }
+    }
 }
 
 
diff --git a/src/com/android/contacts/ContactsListActivity.java b/src/com/android/contacts/ContactsListActivity.java
index b85a191..6020710 100644
--- a/src/com/android/contacts/ContactsListActivity.java
+++ b/src/com/android/contacts/ContactsListActivity.java
@@ -57,8 +57,6 @@
 import android.net.Uri;
 import android.net.Uri.Builder;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
 import android.os.Parcelable;
 import android.preference.PreferenceManager;
 import android.provider.ContactsContract;
@@ -92,57 +90,53 @@
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewParent;
 import android.view.ContextMenu.ContextMenuInfo;
+import android.view.View.OnFocusChangeListener;
+import android.view.View.OnTouchListener;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.AbsListView;
 import android.widget.AdapterView;
 import android.widget.ArrayAdapter;
 import android.widget.Filter;
-import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.ListView;
 import android.widget.QuickContactBadge;
 import android.widget.ResourceCursorAdapter;
 import android.widget.SectionIndexer;
-import android.widget.TabHost;
 import android.widget.TextView;
 import android.widget.AbsListView.OnScrollListener;
 
-import java.lang.ref.SoftReference;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Random;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
 
 /**
  * Displays a list of contacts. Usually is embedded into the ContactsActivity.
  */
 @SuppressWarnings("deprecation")
 public class ContactsListActivity extends ListActivity implements View.OnCreateContextMenuListener,
-        View.OnClickListener, View.OnKeyListener, TextWatcher, TextView.OnEditorActionListener {
+        View.OnClickListener, View.OnKeyListener, TextWatcher, TextView.OnEditorActionListener,
+        OnFocusChangeListener, OnTouchListener {
 
     public static class JoinContactActivity extends ContactsListActivity {
 
     }
 
+    public static class ContactsSearchActivity extends ContactsListActivity {
+
+    }
+
     private static final String TAG = "ContactsListActivity";
 
     private static final boolean ENABLE_ACTION_ICON_OVERLAYS = true;
 
     private static final String LIST_STATE_KEY = "liststate";
-
-    /**
-     * Saved state key for the flag that indicates if the UI is in the search mode.
-     */
-    private static final String SEARCH_MODE_KEY = "searchMode";
+    private static final String SHORTCUT_ACTION_KEY = "shortcutAction";
 
     static final int MENU_ITEM_VIEW_CONTACT = 1;
     static final int MENU_ITEM_CALL = 2;
@@ -157,6 +151,7 @@
     private static final int SUBACTIVITY_VIEW_CONTACT = 2;
     private static final int SUBACTIVITY_DISPLAY_GROUP = 3;
     private static final int SUBACTIVITY_SEARCH = 4;
+    private static final int SUBACTIVITY_FILTER = 5;
 
     private static final int TEXT_HIGHLIGHTING_ANIMATION_DURATION = 350;
 
@@ -238,36 +233,41 @@
     /** Show all contacts and pick them when clicking, and allow creating a new contact */
     static final int MODE_INSERT_OR_EDIT_CONTACT = 45 | MODE_MASK_PICKER | MODE_MASK_CREATE_NEW;
     /** Show all phone numbers and pick them when clicking */
-    // TODO fix and reenable search in phone number picker
-    static final int MODE_PICK_PHONE = 50 | MODE_MASK_PICKER | MODE_MASK_NO_PRESENCE |
-            MODE_MASK_NO_FILTER;
+    static final int MODE_PICK_PHONE = 50 | MODE_MASK_PICKER | MODE_MASK_NO_PRESENCE;
     /** Show all phone numbers through the legacy provider and pick them when clicking */
     static final int MODE_LEGACY_PICK_PHONE =
             51 | MODE_MASK_PICKER | MODE_MASK_NO_PRESENCE | MODE_MASK_NO_FILTER;
     /** Show all postal addresses and pick them when clicking */
     static final int MODE_PICK_POSTAL =
-            55 | MODE_MASK_PICKER | MODE_MASK_NO_PRESENCE | MODE_MASK_NO_FILTER;
+            55 | MODE_MASK_PICKER | MODE_MASK_NO_PRESENCE;
     /** Show all postal addresses and pick them when clicking */
     static final int MODE_LEGACY_PICK_POSTAL =
             56 | MODE_MASK_PICKER | MODE_MASK_NO_PRESENCE | MODE_MASK_NO_FILTER;
     static final int MODE_GROUP = 57 | MODE_MASK_SHOW_PHOTOS;
     /** Run a search query */
-    static final int MODE_QUERY = 60 | MODE_MASK_NO_FILTER | MODE_MASK_SHOW_NUMBER_OF_CONTACTS;
+    static final int MODE_QUERY = 60 | MODE_MASK_SHOW_PHOTOS | MODE_MASK_NO_FILTER
+            | MODE_MASK_SHOW_NUMBER_OF_CONTACTS;
     /** Run a search query in PICK mode, but that still launches to VIEW */
-    static final int MODE_QUERY_PICK_TO_VIEW = 65 | MODE_MASK_NO_FILTER | MODE_MASK_PICKER;
+    static final int MODE_QUERY_PICK_TO_VIEW = 65 | MODE_MASK_SHOW_PHOTOS | MODE_MASK_PICKER
+            | MODE_MASK_SHOW_NUMBER_OF_CONTACTS;
 
     /** Show join suggestions followed by an A-Z list */
     static final int MODE_JOIN_CONTACT = 70 | MODE_MASK_PICKER | MODE_MASK_NO_PRESENCE
             | MODE_MASK_NO_DATA | MODE_MASK_SHOW_PHOTOS | MODE_MASK_DISABLE_QUIKCCONTACT;
 
     /** Run a search query in a PICK mode */
-    static final int MODE_QUERY_PICK = 75 | MODE_MASK_NO_FILTER | MODE_MASK_PICKER;
+    static final int MODE_QUERY_PICK = 75 | MODE_MASK_SHOW_PHOTOS | MODE_MASK_NO_FILTER
+            | MODE_MASK_PICKER | MODE_MASK_DISABLE_QUIKCCONTACT | MODE_MASK_SHOW_NUMBER_OF_CONTACTS;
+
+    /** Run a search query in a PICK_PHONE mode */
+    static final int MODE_QUERY_PICK_PHONE = 80 | MODE_MASK_NO_FILTER | MODE_MASK_PICKER
+            | MODE_MASK_SHOW_NUMBER_OF_CONTACTS;
 
     /**
      * An action used to do perform search while in a contact picker.  It is initiated
      * by the ContactListActivity itself.
      */
-    private static final String ACTION_INTERNAL_SEARCH = "com.android.contacts.INTERNAL_SEARCH";
+    private static final String ACTION_SEARCH_FOR_SHORTCUT = "com.android.contacts.INTERNAL_SEARCH";
 
     /** Maximum number of suggestions shown for joining aggregates */
     static final int MAX_SUGGESTIONS = 4;
@@ -395,8 +395,6 @@
 
     private String mShortcutAction;
 
-    private int mScrollState;
-
     /**
      * Internal query type when in mode {@link #MODE_QUERY_PICK_TO_VIEW}.
      */
@@ -406,11 +404,10 @@
     private static final int QUERY_MODE_MAILTO = 1;
     private static final int QUERY_MODE_TEL = 2;
 
-    /**
-     * Data to use when in mode {@link #MODE_QUERY_PICK_TO_VIEW}. Usually
-     * provided by scheme-specific part of incoming {@link Intent#getData()}.
-     */
-    private String mQueryData;
+    private boolean mSearchMode;
+    private boolean mShowNumberOfContacts;
+
+    private String mInitialFilter;
 
     private static final String CLAUSE_ONLY_VISIBLE = Contacts.IN_VISIBLE_GROUP + "=1";
     private static final String CLAUSE_ONLY_PHONES = Contacts.HAS_PHONE_NUMBER + "=1";
@@ -431,7 +428,6 @@
     private static final UriMatcher sContactsIdMatcher;
 
     private ContactPhotoLoader mPhotoLoader;
-    private static ExecutorService sImageFetchThreadPool;
 
     static {
         sContactsIdMatcher = new UriMatcher(UriMatcher.NO_MATCH);
@@ -489,10 +485,6 @@
     private int mSortOrder;
     private boolean mHighlightWhenScrolling;
     private TextHighlightingAnimation mHighlightingAnimation;
-
-    // If true, the activity is in the "search mode" with the search UI displayed.
-    private boolean mSearchMode;
-    private View mSearchView;
     private SearchEditText mSearchEditText;
 
     /**
@@ -508,24 +500,48 @@
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
-        // Resolve the intent
-        final Intent intent = getIntent();
-
         mIconSize = getResources().getDimensionPixelSize(android.R.dimen.app_icon_size);
         mContactsPrefs = new ContactsPreferences(this);
         mPhotoLoader = new ContactPhotoLoader(this, R.drawable.ic_contact_list_picture);
 
+        // Resolve the intent
+        final Intent intent = getIntent();
+
         // Allow the title to be set to a custom String using an extra on the intent
         String title = intent.getStringExtra(UI.TITLE_EXTRA_KEY);
         if (title != null) {
             setTitle(title);
         }
 
-        final String action = intent.getAction();
-        mMode = MODE_UNKNOWN;
+        String action = intent.getAction();
+        String component = intent.getComponent().getClassName();
+
+        // When we get a FILTER_CONTACTS_ACTION, it represents search in the context
+        // of some other action. Let's retrieve the original action to provide proper
+        // context for the search queries.
+        if (UI.FILTER_CONTACTS_ACTION.equals(action)) {
+            mSearchMode = true;
+            Bundle extras = intent.getExtras();
+            if (extras != null) {
+                mInitialFilter = extras.getString(UI.FILTER_TEXT_EXTRA_KEY);
+                String originalAction =
+                        extras.getString(ContactsSearchManager.ORIGINAL_ACTION_EXTRA_KEY);
+                if (originalAction != null) {
+                    action = originalAction;
+                }
+                String originalComponent =
+                        extras.getString(ContactsSearchManager.ORIGINAL_COMPONENT_EXTRA_KEY);
+                if (originalComponent != null) {
+                    component = originalComponent;
+                }
+            } else {
+                mInitialFilter = null;
+            }
+        }
 
         Log.i(TAG, "Called with action: " + action);
-        if (UI.LIST_DEFAULT.equals(action)) {
+        mMode = MODE_UNKNOWN;
+        if (UI.LIST_DEFAULT.equals(action) || UI.FILTER_CONTACTS_ACTION.equals(action)) {
             mMode = MODE_DEFAULT;
             // When mDefaultMode is true the mode is set in onResume(), since the preferneces
             // activity may change it whenever this activity isn't running
@@ -541,11 +557,11 @@
             mMode = MODE_CUSTOM;
             mDisplayOnlyPhones = false;
         } else if (UI.LIST_STARRED_ACTION.equals(action)) {
-            mMode = MODE_STARRED;
+            mMode = mSearchMode ? MODE_DEFAULT : MODE_STARRED;
         } else if (UI.LIST_FREQUENT_ACTION.equals(action)) {
-            mMode = MODE_FREQUENT;
+            mMode = mSearchMode ? MODE_DEFAULT : MODE_FREQUENT;
         } else if (UI.LIST_STREQUENT_ACTION.equals(action)) {
-            mMode = MODE_STREQUENT;
+            mMode = mSearchMode ? MODE_DEFAULT : MODE_STREQUENT;
         } else if (UI.LIST_CONTACTS_WITH_PHONES_ACTION.equals(action)) {
             mMode = MODE_CUSTOM;
             mDisplayOnlyPhones = true;
@@ -567,14 +583,18 @@
                 mMode = MODE_LEGACY_PICK_POSTAL;
             }
         } else if (Intent.ACTION_CREATE_SHORTCUT.equals(action)) {
-            if (intent.getComponent().getClassName().equals("alias.DialShortcut")) {
+            if (component.equals("alias.DialShortcut")) {
                 mMode = MODE_PICK_PHONE;
                 mShortcutAction = Intent.ACTION_CALL;
                 setTitle(R.string.callShortcutActivityTitle);
-            } else if (intent.getComponent().getClassName().equals("alias.MessageShortcut")) {
+            } else if (component.equals("alias.MessageShortcut")) {
                 mMode = MODE_PICK_PHONE;
                 mShortcutAction = Intent.ACTION_SENDTO;
                 setTitle(R.string.messageShortcutActivityTitle);
+            } else if (mSearchMode) {
+                mMode = MODE_PICK_CONTACT;
+                mShortcutAction = Intent.ACTION_VIEW;
+                setTitle(R.string.shortcutActivityTitle);
             } else {
                 mMode = MODE_PICK_OR_CREATE_CONTACT;
                 mShortcutAction = Intent.ACTION_VIEW;
@@ -583,7 +603,11 @@
         } else if (Intent.ACTION_GET_CONTENT.equals(action)) {
             final String type = intent.resolveType(this);
             if (Contacts.CONTENT_ITEM_TYPE.equals(type)) {
-                mMode = MODE_PICK_OR_CREATE_CONTACT;
+                if (mSearchMode) {
+                    mMode = MODE_PICK_CONTACT;
+                } else {
+                    mMode = MODE_PICK_OR_CREATE_CONTACT;
+                }
             } else if (Phone.CONTENT_ITEM_TYPE.equals(type)) {
                 mMode = MODE_PICK_PHONE;
             } else if (Phones.CONTENT_ITEM_TYPE.equals(type)) {
@@ -593,7 +617,11 @@
             } else if (ContactMethods.CONTENT_POSTAL_ITEM_TYPE.equals(type)) {
                 mMode = MODE_LEGACY_PICK_POSTAL;
             }  else if (People.CONTENT_ITEM_TYPE.equals(type)) {
-                mMode = MODE_LEGACY_PICK_OR_CREATE_PERSON;
+                if (mSearchMode) {
+                    mMode = MODE_LEGACY_PICK_PERSON;
+                } else {
+                    mMode = MODE_LEGACY_PICK_OR_CREATE_PERSON;
+                }
             }
 
         } else if (Intent.ACTION_INSERT_OR_EDIT.equals(action)) {
@@ -615,19 +643,28 @@
             if (intent.hasExtra(Insert.EMAIL)) {
                 mMode = MODE_QUERY_PICK_TO_VIEW;
                 mQueryMode = QUERY_MODE_MAILTO;
-                mQueryData = intent.getStringExtra(Insert.EMAIL);
+                mInitialFilter = intent.getStringExtra(Insert.EMAIL);
             } else if (intent.hasExtra(Insert.PHONE)) {
                 mMode = MODE_QUERY_PICK_TO_VIEW;
                 mQueryMode = QUERY_MODE_TEL;
-                mQueryData = intent.getStringExtra(Insert.PHONE);
+                mInitialFilter = intent.getStringExtra(Insert.PHONE);
             } else {
                 // Otherwise handle the more normal search case
                 mMode = MODE_QUERY;
-                mQueryData = getIntent().getStringExtra(SearchManager.QUERY);
+                mInitialFilter = getIntent().getStringExtra(SearchManager.QUERY);
             }
-        } else if (ACTION_INTERNAL_SEARCH.equals(action)) {
-            mMode = MODE_QUERY_PICK;
-            mQueryData = getIntent().getStringExtra(SearchManager.QUERY);
+        } else if (ACTION_SEARCH_FOR_SHORTCUT.equals(action)) {
+            // The search request has extras to specify query
+            mShortcutAction = intent.getStringExtra(SHORTCUT_ACTION_KEY);
+            if (intent.hasExtra(Insert.PHONE)) {
+                mMode = MODE_QUERY_PICK_PHONE;
+                mQueryMode = QUERY_MODE_TEL;
+                mInitialFilter = intent.getStringExtra(Insert.PHONE);
+            } else {
+                mMode = MODE_QUERY_PICK;
+                mQueryMode = QUERY_MODE_NONE;
+                mInitialFilter = getIntent().getStringExtra(SearchManager.QUERY);
+            }
 
         // Since this is the filter activity it receives all intents
         // dispatched from the SearchManager for security reasons
@@ -673,13 +710,17 @@
         }
 
         if (JOIN_AGGREGATE.equals(action)) {
-            mMode = MODE_JOIN_CONTACT;
-            mQueryAggregateId = intent.getLongExtra(EXTRA_AGGREGATE_ID, -1);
-            if (mQueryAggregateId == -1) {
-                Log.e(TAG, "Intent " + action + " is missing required extra: "
-                        + EXTRA_AGGREGATE_ID);
-                setResult(RESULT_CANCELED);
-                finish();
+            if (mSearchMode) {
+                mMode = MODE_PICK_CONTACT;
+            } else {
+                mMode = MODE_JOIN_CONTACT;
+                mQueryAggregateId = intent.getLongExtra(EXTRA_AGGREGATE_ID, -1);
+                if (mQueryAggregateId == -1) {
+                    Log.e(TAG, "Intent " + action + " is missing required extra: "
+                            + EXTRA_AGGREGATE_ID);
+                    setResult(RESULT_CANCELED);
+                    finish();
+                }
             }
         }
 
@@ -687,6 +728,10 @@
             mMode = MODE_DEFAULT;
         }
 
+        if ((mMode & MODE_MASK_SHOW_NUMBER_OF_CONTACTS) != 0 || mSearchMode) {
+            mShowNumberOfContacts = true;
+        }
+
         if (mMode == MODE_JOIN_CONTACT) {
             setContentView(R.layout.contacts_list_content_join);
             TextView blurbView = (TextView)findViewById(R.id.join_contact_blurb);
@@ -695,12 +740,16 @@
                     getContactDisplayName(mQueryAggregateId));
             blurbView.setText(blurb);
             mJoinModeShowAllContacts = true;
+        } else if (mSearchMode) {
+            setContentView(R.layout.contacts_search_content);
         } else {
             setContentView(R.layout.contacts_list_content);
         }
 
         setupListView();
-        setupSearchView();
+        if (mSearchMode) {
+            setupSearchView();
+        }
 
         mQueryHandler = new QueryHandler(this);
         mJustCreated = true;
@@ -735,12 +784,9 @@
         // Tell list view to not show dividers. We'll do it ourself so that we can *not* show
         // them when an A-Z headers is visible.
         list.setDividerHeight(0);
-        list.setFocusable(true);
+        list.setFocusable((mMode & MODE_MASK_NO_FILTER) == 0);
         list.setOnCreateContextMenuListener(this);
 
-        // Set the proper empty string
-        setEmptyText();
-
         mAdapter = new ContactItemListAdapter(this);
         setListAdapter(mAdapter);
 
@@ -754,6 +800,8 @@
 
         list.setOnScrollListener(mAdapter);
         list.setOnKeyListener(this);
+        list.setOnFocusChangeListener(this);
+        list.setOnTouchListener(this);
 
         // We manually save/restore the listview state
         list.setSaveEnabled(false);
@@ -763,23 +811,10 @@
      * Configures search UI.
      */
     private void setupSearchView() {
-        if ((mMode & MODE_MASK_NO_FILTER) == 0) {
-            mSearchView = findViewById(R.id.searchView);
-            mSearchEditText = (SearchEditText)mSearchView.findViewById(R.id.search_src_text);
-            mSearchEditText.addTextChangedListener(this);
-            mSearchEditText.setOnEditorActionListener(this);
-
-            ImageButton searchButton = (ImageButton)mSearchView.findViewById(R.id.search_btn);
-            searchButton.setOnClickListener(this);
-        }
-    }
-
-    private boolean isPickerMode() {
-        return mMode == MODE_PICK_CONTACT
-                || mMode == MODE_PICK_OR_CREATE_CONTACT
-                || mMode == MODE_LEGACY_PICK_PERSON
-                || mMode == MODE_LEGACY_PICK_OR_CREATE_PERSON
-                || mMode == MODE_QUERY_PICK;
+        mSearchEditText = (SearchEditText)findViewById(R.id.search_src_text);
+        mSearchEditText.addTextChangedListener(this);
+        mSearchEditText.setOnEditorActionListener(this);
+        mSearchEditText.setText(mInitialFilter);
     }
 
     private String getContactDisplayName(long contactId) {
@@ -804,7 +839,6 @@
         return contactName;
     }
 
-
     private int getSummaryDisplayNameColumnIndex() {
         if (mDisplayOrder == ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY) {
             return SUMMARY_DISPLAY_NAME_PRIMARY_COLUMN_INDEX;
@@ -826,23 +860,16 @@
                 }
                 break;
             }
-            case R.id.search_btn: {
-                doSearch();
-                break;
-            }
         }
     }
 
     private void setEmptyText() {
-        if (mMode == MODE_JOIN_CONTACT) {
+        if (mMode == MODE_JOIN_CONTACT || mSearchMode) {
             return;
         }
 
         TextView empty = (TextView) findViewById(R.id.emptyText);
-
-        if (mSearchMode) {
-            empty.setText(getText(R.string.noMatchingFilteredContacts));
-        } else if (mDisplayOnlyPhones) {
+        if (mDisplayOnlyPhones) {
             empty.setText(getText(R.string.noContactsWithPhoneNumbers));
         } else if (mMode == MODE_STREQUENT || mMode == MODE_STARRED) {
             empty.setText(getText(R.string.noFavoritesHelpText));
@@ -881,9 +908,6 @@
 
         mDisplayOnlyPhones = prefs.getBoolean(Prefs.DISPLAY_ONLY_PHONES,
                 Prefs.DISPLAY_ONLY_PHONES_DEFAULT);
-
-        // Update the empty text view with the proper string, as the group may have changed
-        setEmptyText();
     }
 
     @Override
@@ -897,8 +921,6 @@
         super.onResume();
         mPhotoLoader.resume();
 
-        mScrollState = OnScrollListener.SCROLL_STATE_IDLE;
-        boolean runQuery = true;
         Activity parent = getParent();
 
         // Do this before setting the filter. The filter thread relies
@@ -909,11 +931,12 @@
             setDefaultMode();
         }
 
+        // See if we were invoked with a filter
         if (mSearchMode) {
-            startSearchMode(false);
+            mSearchEditText.requestFocus();
         }
 
-        if (mJustCreated && runQuery) {
+        if (mJustCreated) {
             // We need to start a query here the first time the activity is launched, as long
             // as we aren't doing a filter.
             startQuery();
@@ -928,12 +951,6 @@
         return null;
     }
 
-    private void setTextFilter(String filterText) {
-        if (mSearchEditText != null) {
-            mSearchEditText.setText(filterText);
-        }
-    }
-
     @Override
     protected void onRestart() {
         super.onRestart();
@@ -953,8 +970,9 @@
     protected void onSaveInstanceState(Bundle icicle) {
         super.onSaveInstanceState(icicle);
         // Save list state in the bundle so we can restore it after the QueryHandler has run
-        icicle.putParcelable(LIST_STATE_KEY, mList.onSaveInstanceState());
-        icicle.putBoolean(SEARCH_MODE_KEY, mSearchMode);
+        if (mList != null) {
+            icicle.putParcelable(LIST_STATE_KEY, mList.onSaveInstanceState());
+        }
     }
 
     @Override
@@ -962,7 +980,6 @@
         super.onRestoreInstanceState(icicle);
         // Retrieve list state. This will be applied after the QueryHandler has run
         mListState = icicle.getParcelable(LIST_STATE_KEY);
-        mSearchMode = icicle.getBoolean(SEARCH_MODE_KEY);
     }
 
     @Override
@@ -1010,7 +1027,7 @@
                 return true;
             }
             case R.id.menu_search: {
-                startSearchMode(true);
+                onSearchRequested();
                 return true;
             }
             case R.id.menu_add: {
@@ -1034,69 +1051,21 @@
         return false;
     }
 
-    /**
-     * Displays and initializes the search UI at the top of the activity.  If
-     * this activity is part of a tab activity, also removes the tabs.
-     *
-     * @param showKeyboard a flag indicating whether the soft keyboard should be
-     *            auto shown automatically.
-     */
-    private void startSearchMode(boolean showKeyboard) {
-        View tabs = findTabWidget();
-        if (tabs != null) {
-            tabs.setVisibility(View.GONE);
-        }
-
-        mList.setFocusable(false);
-        mSearchEditText.setAutoShowKeyboard(showKeyboard);
-        mSearchEditText.requestFocus();
-        mSearchView.setVisibility(View.VISIBLE);
-        mSearchMode = true;
-        setEmptyText();
-    }
-
-    /**
-     * Hides the search UI and shows the tabs if they were hidden before.
-     */
-    private void stopSearchMode() {
-
-        // In case the list view owns the soft keyboard at this point, hide the keyboard
-        InputMethodManager inputManager = (InputMethodManager)getSystemService(
-                Context.INPUT_METHOD_SERVICE);
-        inputManager.hideSoftInputFromWindow(mList.getWindowToken(), 0);
-
-        // In case the search text view owns the soft keyboard, do the same
-        mSearchEditText.hideKeyboard();
-        mSearchView.setVisibility(View.GONE);
-
-        View tabs = findTabWidget();
-        if (tabs != null) {
-            tabs.setVisibility(View.VISIBLE);
-        }
-
-        mSearchMode = false;
-        setEmptyText();
-
-        // This will trigger a query
-        setTextFilter(null);
-
-        mList.setFocusable(true);
-    }
-
-    /**
-     * If this activity is hosted by a tab activity, the method returns the
-     * TabWidget from the TabHost activity; otherwise it returns null.
-     */
-    private View findTabWidget() {
-        View start = getListView();
-        ViewParent parent = start.getParent();
-        while (parent != null) {
-            if (parent instanceof TabHost) {
-                return ((TabHost)parent).getTabWidget();
+    @Override
+    public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
+            boolean globalSearch) {
+        if (globalSearch) {
+            super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
+        } else {
+            if (!mSearchMode && (mMode & MODE_MASK_NO_FILTER) == 0) {
+                if ((mMode & MODE_MASK_PICKER) != 0) {
+                    ContactsSearchManager.startSearchForResult(this, initialQuery,
+                            SUBACTIVITY_FILTER);
+                } else {
+                    ContactsSearchManager.startSearch(this, initialQuery);
+                }
             }
-            parent = parent.getParent();
         }
-        return null;
     }
 
     /**
@@ -1104,23 +1073,14 @@
      * search text edit.
      */
     protected void onSearchTextChanged() {
+        // Set the proper empty string
+        setEmptyText();
+
         Filter filter = mAdapter.getFilter();
         filter.filter(getTextFilter());
     }
 
     /**
-     * Closes search UI if shown, otherwise follows the default "back" behavior.
-     */
-    @Override
-    public void onBackPressed() {
-        if (mSearchMode) {
-            stopSearchMode();
-        } else {
-            super.onBackPressed();
-        }
-    }
-
-    /**
      * Starts a new activity that will run a search query and display search results.
      */
     private void doSearch() {
@@ -1129,10 +1089,26 @@
             return;
         }
 
-        Intent intent = new Intent(this, getClass());
+        Intent intent = new Intent(this, ContactsListActivity.class);
         intent.putExtra(SearchManager.QUERY, query);
-        if (isPickerMode()) {
-            intent.setAction(ACTION_INTERNAL_SEARCH);
+        if ((mMode & MODE_MASK_PICKER) != 0) {
+            intent.setAction(ACTION_SEARCH_FOR_SHORTCUT);
+            intent.putExtra(SHORTCUT_ACTION_KEY, mShortcutAction);
+            if (mShortcutAction != null) {
+                if (Intent.ACTION_CALL.equals(mShortcutAction)
+                        || Intent.ACTION_SENDTO.equals(mShortcutAction)) {
+                    intent.putExtra(Insert.PHONE, query);
+                }
+            } else {
+                switch (mQueryMode) {
+                    case QUERY_MODE_MAILTO:
+                        intent.putExtra(Insert.EMAIL, query);
+                        break;
+                    case QUERY_MODE_TEL:
+                        intent.putExtra(Insert.PHONE, query);
+                        break;
+                }
+            }
             startActivityForResult(intent, SUBACTIVITY_SEARCH);
         } else {
             intent.setAction(Intent.ACTION_SEARCH);
@@ -1301,10 +1277,12 @@
                 mJustCreated = true;
                 break;
 
+            case SUBACTIVITY_FILTER:
             case SUBACTIVITY_SEARCH:
+                // Pass through results of filter or search UI
                 if (resultCode == RESULT_OK) {
-                    returnPickerResult(null, data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME),
-                            data.getData());
+                    setResult(RESULT_OK, data);
+                    finish();
                 }
                 break;
         }
@@ -1416,8 +1394,7 @@
         if (!mSearchMode && (mMode & MODE_MASK_NO_FILTER) == 0) {
             int unicodeChar = event.getUnicodeChar();
             if (unicodeChar != 0) {
-                setTextFilter(new String(new int[]{unicodeChar}, 0, 1));
-                startSearchMode(false);
+                startSearch(new String(new int[]{unicodeChar}, 0, 1), false, null, false);
                 return true;
             }
         }
@@ -1441,8 +1418,11 @@
      * Event handler for search UI.
      */
     public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
-        if (actionId == EditorInfo.IME_ACTION_GO) {
-            doSearch();
+        if (actionId == EditorInfo.IME_ACTION_DONE) {
+            hideSoftKeyboard();
+            if (TextUtils.isEmpty(getTextFilter())) {
+                finish();
+            }
             return true;
         }
         return false;
@@ -1467,19 +1447,6 @@
                 }
                 break;
             }
-
-            case KeyEvent.KEYCODE_SEARCH: {
-                if ((mMode & MODE_MASK_NO_FILTER) == 0) {
-                    if (mSearchMode) {
-                        stopSearchMode();
-                    } else {
-                        startSearchMode(true);
-                    }
-                    return true;
-                } else {
-                    return false;
-                }
-            }
         }
 
         return super.onKeyDown(keyCode, event);
@@ -1525,12 +1492,40 @@
         }
     }
 
+    /**
+     * Dismisses the soft keyboard when the list takes focus.
+     */
+    public void onFocusChange(View view, boolean hasFocus) {
+        if (view == getListView() && hasFocus) {
+            hideSoftKeyboard();
+        }
+    }
+
+    /**
+     * Dismisses the soft keyboard when the list takes focus.
+     */
+    public boolean onTouch(View view, MotionEvent event) {
+        if (view == getListView()) {
+            hideSoftKeyboard();
+        }
+        return false;
+    }
+
+    /**
+     * Dismisses the search UI along with the keyboard if the filter text is empty.
+     */
+    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
+        if (mSearchMode && keyCode == KeyEvent.KEYCODE_BACK && TextUtils.isEmpty(getTextFilter())) {
+            hideSoftKeyboard();
+            onBackPressed();
+            return true;
+        }
+        return false;
+    }
+
     @Override
     protected void onListItemClick(ListView l, View v, int position, long id) {
-        // Hide soft keyboard, if visible
-        InputMethodManager inputMethodManager = (InputMethodManager)
-                getSystemService(Context.INPUT_METHOD_SERVICE);
-        inputMethodManager.hideSoftInputFromWindow(mList.getWindowToken(), 0);
+        hideSoftKeyboard();
 
         if (mMode == MODE_INSERT_OR_EDIT_CONTACT) {
             Intent intent;
@@ -1560,6 +1555,8 @@
         } else if (mMode == MODE_JOIN_CONTACT && id == JOIN_MODE_SHOW_ALL_CONTACTS_ID) {
             mJoinModeShowAllContacts = false;
             startQuery();
+        } else if (mSearchMode && mAdapter.isSearchAllContactsItemPosition(position)) {
+            doSearch();
         } else if (id > 0) {
             final Uri uri = getSelectedUri(position);
             if ((mMode & MODE_MASK_PICKER) == 0) {
@@ -1572,14 +1569,14 @@
                 final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
                 startActivity(intent);
                 finish();
-            } else if (isPickerMode()) {
-                Cursor c = (Cursor) mAdapter.getItem(position);
-                returnPickerResult(c, c.getString(getSummaryDisplayNameColumnIndex()), uri);
-            } else if (mMode == MODE_PICK_PHONE) {
+            } else if (mMode == MODE_PICK_PHONE || mMode == MODE_QUERY_PICK_PHONE) {
                 Cursor c = (Cursor) mAdapter.getItem(position);
                 long contactId = c.getLong(PHONE_CONTACT_ID_COLUMN_INDEX);
                 returnPickerResult(c, c.getString(PHONE_DISPLAY_NAME_COLUMN_INDEX),
                         ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId));
+            } else if ((mMode & MODE_MASK_PICKER) != 0) {
+                Cursor c = (Cursor) mAdapter.getItem(position);
+                returnPickerResult(c, c.getString(getSummaryDisplayNameColumnIndex()), uri);
             } else if (mMode == MODE_PICK_POSTAL
                     || mMode == MODE_LEGACY_PICK_POSTAL
                     || mMode == MODE_LEGACY_PICK_PHONE) {
@@ -1590,6 +1587,13 @@
         }
     }
 
+    private void hideSoftKeyboard() {
+        // Hide soft keyboard, if visible
+        InputMethodManager inputMethodManager = (InputMethodManager)
+                getSystemService(Context.INPUT_METHOD_SERVICE);
+        inputMethodManager.hideSoftInputFromWindow(mList.getWindowToken(), 0);
+    }
+
     /**
      * @param contactUri In most cases, this should be a lookup {@link Uri}, possibly
      *            generated through {@link Contacts#getLookupUri(long, String)}.
@@ -1835,15 +1839,21 @@
             }
             case MODE_QUERY_PICK_TO_VIEW: {
                 if (mQueryMode == QUERY_MODE_MAILTO) {
-                    return Uri.withAppendedPath(Email.CONTENT_FILTER_URI, Uri.encode(mQueryData));
+                    return Uri.withAppendedPath(Email.CONTENT_FILTER_URI,
+                            Uri.encode(mInitialFilter));
                 } else if (mQueryMode == QUERY_MODE_TEL) {
-                    return Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, Uri.encode(mQueryData));
+                    return Uri.withAppendedPath(Phone.CONTENT_FILTER_URI,
+                            Uri.encode(mInitialFilter));
                 }
                 return CONTACTS_CONTENT_URI_WITH_LETTER_COUNTS;
             }
             case MODE_QUERY:
             case MODE_QUERY_PICK: {
-                return getContactFilterUri(mQueryData);
+                return getContactFilterUri(mInitialFilter);
+            }
+            case MODE_QUERY_PICK_PHONE: {
+                return Uri.withAppendedPath(Phone.CONTENT_FILTER_URI,
+                        Uri.encode(mInitialFilter));
             }
             case MODE_GROUP: {
                 return mGroupUri;
@@ -1895,7 +1905,8 @@
             case MODE_LEGACY_PICK_OR_CREATE_PERSON: {
                 return ContentUris.withAppendedId(People.CONTENT_URI, id);
             }
-            case MODE_PICK_PHONE: {
+            case MODE_PICK_PHONE:
+            case MODE_QUERY_PICK_PHONE: {
                 return ContentUris.withAppendedId(Data.CONTENT_URI, id);
             }
             case MODE_LEGACY_PICK_PHONE: {
@@ -1932,6 +1943,7 @@
             case MODE_LEGACY_PICK_OR_CREATE_PERSON: {
                 return LEGACY_PEOPLE_PROJECTION ;
             }
+            case MODE_QUERY_PICK_PHONE:
             case MODE_PICK_PHONE: {
                 return PHONES_PROJECTION;
             }
@@ -2044,6 +2056,9 @@
     }
 
     void startQuery() {
+        // Set the proper empty string
+        setEmptyText();
+
         mAdapter.setLoading(true);
 
         // Cancel any pending queries
@@ -2065,6 +2080,11 @@
         }
 
         String[] projection = getProjectionForQuery();
+        if (mSearchMode && TextUtils.isEmpty(getTextFilter())) {
+            mAdapter.changeCursor(new MatrixCursor(projection));
+            return;
+        }
+
         String callingPackage = getCallingPackage();
         Uri uri = getUriToQuery();
         if (!TextUtils.isEmpty(callingPackage)) {
@@ -2077,35 +2097,20 @@
         // Kick off the new query
         switch (mMode) {
             case MODE_GROUP:
-                mQueryHandler.startQuery(QUERY_TOKEN, null,
-                        uri, projection, getContactSelection(), null,
-                        getSortOrder(projection));
-                break;
-
             case MODE_DEFAULT:
             case MODE_PICK_CONTACT:
             case MODE_PICK_OR_CREATE_CONTACT:
             case MODE_INSERT_OR_EDIT_CONTACT:
-                mQueryHandler.startQuery(QUERY_TOKEN, null, uri,
-                        projection, getContactSelection(), null,
-                        getSortOrder(projection));
+                mQueryHandler.startQuery(QUERY_TOKEN, null, uri, projection, getContactSelection(),
+                        null, getSortOrder(projection));
                 break;
 
             case MODE_LEGACY_PICK_PERSON:
             case MODE_LEGACY_PICK_OR_CREATE_PERSON:
-                mQueryHandler.startQuery(QUERY_TOKEN, null, uri,
-                        projection, null, null,
-                        getSortOrder(projection));
-                break;
-
+            case MODE_PICK_POSTAL:
             case MODE_QUERY:
-            case MODE_QUERY_PICK: {
-                mQueryHandler.startQuery(QUERY_TOKEN, null, uri,
-                        projection, null, null,
-                        getSortOrder(projection));
-                break;
-            }
-
+            case MODE_QUERY_PICK:
+            case MODE_QUERY_PICK_PHONE:
             case MODE_QUERY_PICK_TO_VIEW: {
                 mQueryHandler.startQuery(QUERY_TOKEN, null, uri, projection, null, null,
                         getSortOrder(projection));
@@ -2133,12 +2138,7 @@
             case MODE_PICK_PHONE:
             case MODE_LEGACY_PICK_PHONE:
                 mQueryHandler.startQuery(QUERY_TOKEN, null, uri,
-                        projection, null, null, getSortOrder(projection));
-                break;
-
-            case MODE_PICK_POSTAL:
-                mQueryHandler.startQuery(QUERY_TOKEN, null, uri,
-                        projection, null, null, getSortOrder(projection));
+                        projection, CLAUSE_ONLY_VISIBLE, null, getSortOrder(projection));
                 break;
 
             case MODE_LEGACY_PICK_POSTAL:
@@ -2163,10 +2163,12 @@
      * @return a cursor with the results of the filter
      */
     Cursor doFilter(String filter) {
-        final ContentResolver resolver = getContentResolver();
-
         String[] projection = getProjectionForQuery();
+        if (mSearchMode && TextUtils.isEmpty(getTextFilter())) {
+            return new MatrixCursor(projection);
+        }
 
+        final ContentResolver resolver = getContentResolver();
         switch (mMode) {
             case MODE_DEFAULT:
             case MODE_PICK_CONTACT:
@@ -2211,7 +2213,7 @@
                 if (!TextUtils.isEmpty(filter)) {
                     uri = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, Uri.encode(filter));
                 }
-                return resolver.query(uri, projection, null, null,
+                return resolver.query(uri, projection, CLAUSE_ONLY_VISIBLE, null,
                         getSortOrder(projection));
             }
 
@@ -2380,7 +2382,7 @@
                     if (activity.mAdapter.mSuggestionsCursorCount == 0
                             || !activity.mJoinModeShowAllContacts) {
                         startQuery(QUERY_TOKEN, null, activity.getContactFilterUri(
-                                        activity.mQueryData),
+                                        activity.getTextFilter()),
                                 CONTACTS_SUMMARY_PROJECTION,
                                 Contacts._ID + " != " + activity.mQueryAggregateId
                                         + " AND " + CLAUSE_ONLY_VISIBLE, null,
@@ -2423,17 +2425,6 @@
         public TextWithHighlighting textWithHighlighting;
     }
 
-    final static class PhotoInfo {
-        public int position;
-        public long photoId;
-
-        public PhotoInfo(int position, long photoId) {
-            this.position = position;
-            this.photoId = photoId;
-        }
-        public QuickContactBadge photoView;
-    }
-
     final static class PinnedHeaderCache {
         public TextView titleView;
         public ColorStateList textColor;
@@ -2460,21 +2451,23 @@
             switch (mMode) {
                 case MODE_LEGACY_PICK_POSTAL:
                 case MODE_PICK_POSTAL:
-                    mDisplaySectionHeaders = false;
-                    break;
                 case MODE_LEGACY_PICK_PHONE:
                 case MODE_PICK_PHONE:
+                case MODE_STREQUENT:
+                case MODE_FREQUENT:
                     mDisplaySectionHeaders = false;
                     break;
-                default:
-                    break;
+            }
+
+            if (mSearchMode) {
+                mDisplaySectionHeaders = false;
             }
 
             // Do not display the second line of text if in a specific SEARCH query mode, usually for
             // matching a specific E-mail or phone number. Any contact details
             // shown would be identical, and columns might not even be present
             // in the returned cursor.
-            if (mQueryMode != QUERY_MODE_NONE) {
+            if (mMode != MODE_QUERY_PICK_PHONE && mQueryMode != QUERY_MODE_NONE) {
                 mDisplayAdditionalData = false;
             }
 
@@ -2490,10 +2483,6 @@
                 mDisplayPhotos = true;
                 setViewResource(R.layout.contacts_list_item_photo);
             }
-
-            if (mMode == MODE_STREQUENT || mMode == MODE_FREQUENT) {
-                mDisplaySectionHeaders = false;
-            }
         }
 
         public boolean getDisplaySectionHeadersEnabled() {
@@ -2536,6 +2525,8 @@
                 // This mode mask adds a header and we always want it to show up, even
                 // if the list is empty, so always claim the list is not empty.
                 return false;
+            } else if (mSearchMode) {
+                return TextUtils.isEmpty(getTextFilter());
             } else {
                 if (mCursor == null || mLoading) {
                     // We don't want the empty state to show when loading.
@@ -2548,18 +2539,23 @@
 
         @Override
         public int getItemViewType(int position) {
-            if (position == 0
-                    && ((mMode & MODE_MASK_SHOW_NUMBER_OF_CONTACTS) != 0
-                            || (mMode & MODE_MASK_CREATE_NEW) != 0)) {
+            if (position == 0 && (mShowNumberOfContacts || (mMode & MODE_MASK_CREATE_NEW) != 0)) {
                 return IGNORE_ITEM_VIEW_TYPE;
             }
+
             if (isShowAllContactsItemPosition(position)) {
                 return IGNORE_ITEM_VIEW_TYPE;
             }
+
+            if (isSearchAllContactsItemPosition(position)) {
+                return IGNORE_ITEM_VIEW_TYPE;
+            }
+
             if (getSeparatorId(position) != 0) {
                 // We don't want the separator view to be recycled.
                 return IGNORE_ITEM_VIEW_TYPE;
             }
+
             return super.getItemViewType(position);
         }
 
@@ -2571,7 +2567,7 @@
             }
 
             // handle the total contacts item
-            if (position == 0 && (mMode & MODE_MASK_SHOW_NUMBER_OF_CONTACTS) != 0) {
+            if (position == 0 && mShowNumberOfContacts) {
                 return getTotalContactCountView(parent);
             }
 
@@ -2585,6 +2581,11 @@
                         inflate(R.layout.contacts_list_show_all_item, parent, false);
             }
 
+            if (isSearchAllContactsItemPosition(position)) {
+                return getLayoutInflater().
+                        inflate(R.layout.contacts_list_search_all_item, parent, false);
+            }
+
             // Handle the separator specially
             int separatorId = getSeparatorId(position);
             if (separatorId != 0) {
@@ -2626,19 +2627,16 @@
             View view = inflater.inflate(R.layout.total_contacts, parent, false);
 
             TextView totalContacts = (TextView) view.findViewById(R.id.totalContactsText);
-            TextView searchForMore = (TextView) view.findViewById(R.id.searchForMoreText);
 
             String text;
             int count = getRealCount();
 
-            if (mMode == MODE_QUERY || mMode == MODE_QUERY_PICK) {
+            if (mMode == MODE_QUERY || mMode == MODE_QUERY_PICK || mMode == MODE_QUERY_PICK_PHONE) {
                 text = getQuantityText(count, R.string.listFoundAllContactsZero,
                         R.plurals.listFoundAllContacts);
-                searchForMore.setVisibility(View.GONE);
             } else if (mSearchMode && !TextUtils.isEmpty(getTextFilter())) {
                 text = getQuantityText(count, R.string.listFoundAllContactsZero,
                         R.plurals.searchFoundContacts);
-                searchForMore.setVisibility(View.VISIBLE);
             } else {
                 if (mDisplayOnlyPhones) {
                     text = getQuantityText(count, R.string.listTotalPhoneContactsZero,
@@ -2647,7 +2645,6 @@
                     text = getQuantityText(count, R.string.listTotalAllContactsZero,
                             R.plurals.listTotalAllContacts);
                 }
-                searchForMore.setVisibility(View.GONE);
             }
             totalContacts.setText(text);
             return view;
@@ -2668,6 +2665,10 @@
                     && mSuggestionsCursorCount != 0 && position == mSuggestionsCursorCount + 2;
         }
 
+        private boolean isSearchAllContactsItemPosition(int position) {
+            return mSearchMode && position == getCount() - 1;
+        }
+
         private int getSeparatorId(int position) {
             int separatorId = 0;
             if (position == mFrequentSeparatorPos) {
@@ -2725,7 +2726,8 @@
             boolean highlightingEnabled = false;
             switch(mMode) {
                 case MODE_PICK_PHONE:
-                case MODE_LEGACY_PICK_PHONE: {
+                case MODE_LEGACY_PICK_PHONE:
+                case MODE_QUERY_PICK_PHONE: {
                     nameColumnIndex = PHONE_DISPLAY_NAME_COLUMN_INDEX;
                     dataColumnIndex = PHONE_NUMBER_COLUMN_INDEX;
                     typeColumnIndex = PHONE_TYPE_COLUMN_INDEX;
@@ -2987,13 +2989,13 @@
         @Override
         public boolean areAllItemsEnabled() {
             return mMode != MODE_STARRED
-                && (mMode & MODE_MASK_SHOW_NUMBER_OF_CONTACTS) == 0
+                && !mShowNumberOfContacts
                 && mSuggestionsCursorCount == 0;
         }
 
         @Override
         public boolean isEnabled(int position) {
-            if ((mMode & MODE_MASK_SHOW_NUMBER_OF_CONTACTS) != 0) {
+            if (mShowNumberOfContacts) {
                 if (position == 0) {
                     return false;
                 }
@@ -3012,11 +3014,17 @@
                 return 0;
             }
             int superCount = super.getCount();
-            if ((mMode & MODE_MASK_SHOW_NUMBER_OF_CONTACTS) != 0 && superCount > 0) {
+            if (mShowNumberOfContacts && (mSearchMode || superCount > 0)) {
                 // We don't want to count this header if it's the only thing visible, so that
                 // the empty text will display.
                 superCount++;
             }
+
+            if (mSearchMode) {
+                // Last element in the list is the "Find
+                superCount++;
+            }
+
             if (mSuggestionsCursorCount != 0) {
                 // When showing suggestions, we have 2 additional list items: the "Suggestions"
                 // and "All contacts" headers.
@@ -3038,7 +3046,7 @@
         }
 
         private int getRealPosition(int pos) {
-            if ((mMode & MODE_MASK_SHOW_NUMBER_OF_CONTACTS) != 0) {
+            if (mShowNumberOfContacts) {
                 pos--;
             }
 
@@ -3106,7 +3114,6 @@
                 }
             }
 
-            mScrollState = scrollState;
             if (scrollState == OnScrollListener.SCROLL_STATE_FLING) {
                 mPhotoLoader.pause();
             } else if (mDisplayPhotos) {
diff --git a/src/com/android/contacts/ContactsSearchManager.java b/src/com/android/contacts/ContactsSearchManager.java
new file mode 100644
index 0000000..8f42c3a
--- /dev/null
+++ b/src/com/android/contacts/ContactsSearchManager.java
@@ -0,0 +1,63 @@
+/*
+ * 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.android.contacts;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Intents.UI;
+
+/**
+ * A convenience class that helps launch contact search from within the app.
+ */
+public class ContactsSearchManager {
+
+    /**
+     * An extra that provides context for search UI and defines the scope for
+     * the search queries.
+     */
+    public static final String ORIGINAL_ACTION_EXTRA_KEY = "originalAction";
+
+    /**
+     * An extra that provides context for search UI and defines the scope for
+     * the search queries.
+     */
+    public static final String ORIGINAL_COMPONENT_EXTRA_KEY = "originalComponent";
+
+    /**
+     * Starts the contact list activity in the search mode.
+     */
+    public static void startSearch(Activity context, String initialQuery) {
+        context.startActivity(buildIntent(context, initialQuery));
+    }
+
+    public static void startSearchForResult(Activity context, String initialQuery,
+            int requestCode) {
+        context.startActivityForResult(buildIntent(context, initialQuery), requestCode);
+    }
+
+    private static Intent buildIntent(Activity context, String initialQuery) {
+        Intent intent = new Intent();
+        intent.setData(ContactsContract.Contacts.CONTENT_URI);
+        intent.setAction(UI.FILTER_CONTACTS_ACTION);
+        intent.putExtra(UI.FILTER_TEXT_EXTRA_KEY, initialQuery);
+        intent.putExtra(ORIGINAL_ACTION_EXTRA_KEY, context.getIntent().getAction());
+        intent.putExtra(ORIGINAL_COMPONENT_EXTRA_KEY,
+                context.getIntent().getComponent().getClassName());
+        return intent;
+    }
+}
diff --git a/src/com/android/contacts/RecentCallsListActivity.java b/src/com/android/contacts/RecentCallsListActivity.java
index 8e553d1..dedc531 100644
--- a/src/com/android/contacts/RecentCallsListActivity.java
+++ b/src/com/android/contacts/RecentCallsListActivity.java
@@ -1083,4 +1083,14 @@
             startActivity(intent);
         }
     }
+
+    @Override
+    public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
+            boolean globalSearch) {
+        if (globalSearch) {
+            super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
+        } else {
+            ContactsSearchManager.startSearch(this, initialQuery);
+        }
+    }
 }
diff --git a/src/com/android/contacts/SearchEditText.java b/src/com/android/contacts/SearchEditText.java
index 74a1d30..7683f23 100644
--- a/src/com/android/contacts/SearchEditText.java
+++ b/src/com/android/contacts/SearchEditText.java
@@ -17,60 +17,53 @@
 package com.android.contacts;
 
 import android.content.Context;
-import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
 import android.util.AttributeSet;
-import android.view.inputmethod.InputMethodManager;
+import android.view.KeyEvent;
 import android.widget.EditText;
 
 /**
- * A custom text editor that optionally automatically brings up the soft
- * keyboard when first focused.
+ * A custom text editor that helps automatically dismiss the activity along with the soft
+ * keyboard.
  */
 public class SearchEditText extends EditText {
 
-    private boolean mAutoShowKeyboard;
+    private boolean mMagnifyingGlassShown = true;
+    private Drawable mMagnifyingGlass;
 
     public SearchEditText(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mMagnifyingGlass = getCompoundDrawables()[2];
     }
 
     /**
-     * Automatically show the soft keyboard when the field gets focus.  This is a
-     * single-shot setting - it is reset as soon as the keyboard is shown.
+     * Conditionally shows a magnifying glass icon on the right side of the text field
+     * when the text it empty.
      */
-    public void setAutoShowKeyboard(boolean flag) {
-        mAutoShowKeyboard = flag;
-    }
-
     @Override
-    public void onWindowFocusChanged(boolean hasWindowFocus) {
-        super.onWindowFocusChanged(hasWindowFocus);
-        if (hasWindowFocus && mAutoShowKeyboard) {
-            showKeyboard();
+    public boolean onPreDraw() {
+        boolean emptyText = TextUtils.isEmpty(getText());
+        if (mMagnifyingGlassShown != emptyText) {
+            mMagnifyingGlassShown = emptyText;
+            if (mMagnifyingGlassShown) {
+                setCompoundDrawables(null, null, mMagnifyingGlass, null);
+            } else {
+                setCompoundDrawables(null, null, null, null);
+            }
+            return false;
         }
-    }
-
-    @Override
-    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
-        super.onFocusChanged(focused, direction, previouslyFocusedRect);
-        if (focused && mAutoShowKeyboard) {
-            showKeyboard();
-        }
+        return super.onPreDraw();
     }
 
     /**
-     * Explicitly brings up the soft keyboard if necessary.
+     * Forwards the onKeyPreIme call to the view's activity.
      */
-    private void showKeyboard() {
-        InputMethodManager inputManager = (InputMethodManager)getContext().getSystemService(
-                Context.INPUT_METHOD_SERVICE);
-        inputManager.showSoftInput(this, 0);
-        mAutoShowKeyboard = false;
-    }
-
-    public void hideKeyboard() {
-        InputMethodManager inputManager = (InputMethodManager)getContext().getSystemService(
-                Context.INPUT_METHOD_SERVICE);
-        inputManager.hideSoftInputFromWindow(getWindowToken(), 0);
+    @Override
+    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
+        if (((ContactsListActivity)getContext()).onKeyPreIme(keyCode, event)) {
+            return true;
+        }
+        return super.onKeyPreIme(keyCode, event);
     }
 }
diff --git a/src/com/android/contacts/TwelveKeyDialer.java b/src/com/android/contacts/TwelveKeyDialer.java
index c0390eb..1ef3886 100644
--- a/src/com/android/contacts/TwelveKeyDialer.java
+++ b/src/com/android/contacts/TwelveKeyDialer.java
@@ -16,6 +16,10 @@
 
 package com.android.contacts;
 
+import com.android.internal.telephony.ITelephony;
+import com.android.phone.CallLogAsync;
+import com.android.phone.HapticFeedback;
+
 import android.app.Activity;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
@@ -29,22 +33,19 @@
 import android.media.ToneGenerator;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
-import android.provider.Contacts.Intents.Insert;
+import android.provider.Settings;
 import android.provider.Contacts.People;
 import android.provider.Contacts.Phones;
 import android.provider.Contacts.PhonesColumns;
-import android.provider.Settings;
+import android.provider.Contacts.Intents.Insert;
 import android.telephony.PhoneNumberFormattingTextWatcher;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.PhoneStateListener;
 import android.telephony.TelephonyManager;
 import android.text.Editable;
-import android.text.SpannableStringBuilder;
 import android.text.TextUtils;
 import android.text.TextWatcher;
 import android.text.method.DialerKeyListener;
@@ -64,13 +65,10 @@
 import android.widget.ListView;
 import android.widget.TextView;
 
-import com.android.internal.telephony.ITelephony;
-import com.android.phone.CallLogAsync;
-import com.android.phone.HapticFeedback;
-
 /**
  * Dialer activity that displays the typical twelve key interface.
  */
+@SuppressWarnings("deprecation")
 public class TwelveKeyDialer extends Activity implements View.OnClickListener,
         View.OnLongClickListener, View.OnKeyListener,
         AdapterView.OnItemClickListener, TextWatcher {
@@ -1235,4 +1233,14 @@
                     });
         mCallLog.getLastOutgoingCall(lastCallArgs);
     }
+
+    @Override
+    public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
+            boolean globalSearch) {
+        if (globalSearch) {
+            super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
+        } else {
+            ContactsSearchManager.startSearch(this, initialQuery);
+        }
+    }
 }
diff --git a/src/com/android/contacts/ViewContactActivity.java b/src/com/android/contacts/ViewContactActivity.java
index 8402b3f..2b0c594 100644
--- a/src/com/android/contacts/ViewContactActivity.java
+++ b/src/com/android/contacts/ViewContactActivity.java
@@ -1259,4 +1259,14 @@
 
         final int _ID = 0;
     }
+
+    @Override
+    public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
+            boolean globalSearch) {
+        if (globalSearch) {
+            super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
+        } else {
+            ContactsSearchManager.startSearch(this, initialQuery);
+        }
+    }
 }
diff --git a/src/com/android/contacts/ui/ContactsPreferencesActivity.java b/src/com/android/contacts/ui/ContactsPreferencesActivity.java
index d9491be..a2edd8d 100644
--- a/src/com/android/contacts/ui/ContactsPreferencesActivity.java
+++ b/src/com/android/contacts/ui/ContactsPreferencesActivity.java
@@ -16,6 +16,7 @@
 
 package com.android.contacts.ui;
 
+import com.android.contacts.ContactsSearchManager;
 import com.android.contacts.R;
 import com.android.contacts.model.ContactsSource;
 import com.android.contacts.model.GoogleSource;
@@ -1054,4 +1055,14 @@
             context.stopService(new Intent(context, EmptyService.class));
         }
     }
+
+    @Override
+    public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
+            boolean globalSearch) {
+        if (globalSearch) {
+            super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
+        } else {
+            ContactsSearchManager.startSearch(this, initialQuery);
+        }
+    }
 }
diff --git a/src/com/android/contacts/ui/EditContactActivity.java b/src/com/android/contacts/ui/EditContactActivity.java
index 89f9b12..92fdadb 100644
--- a/src/com/android/contacts/ui/EditContactActivity.java
+++ b/src/com/android/contacts/ui/EditContactActivity.java
@@ -16,6 +16,7 @@
 
 package com.android.contacts.ui;
 
+import com.android.contacts.ContactsSearchManager;
 import com.android.contacts.ContactsListActivity;
 import com.android.contacts.ContactsUtils;
 import com.android.contacts.R;
@@ -1305,4 +1306,14 @@
         long twoId = twoValues.getAsLong(RawContacts._ID);
         return (int)(oneId - twoId);
     }
+
+    @Override
+    public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
+            boolean globalSearch) {
+        if (globalSearch) {
+            super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
+        } else {
+            ContactsSearchManager.startSearch(this, initialQuery);
+        }
+    }
 }
diff --git a/src/com/android/contacts/ui/ShowOrCreateActivity.java b/src/com/android/contacts/ui/ShowOrCreateActivity.java
index 0828b3f..e0781d2 100755
--- a/src/com/android/contacts/ui/ShowOrCreateActivity.java
+++ b/src/com/android/contacts/ui/ShowOrCreateActivity.java
@@ -16,6 +16,7 @@
 
 package com.android.contacts.ui;
 
+import com.android.contacts.ContactsSearchManager;
 import com.android.contacts.ContactsListActivity;
 import com.android.contacts.R;
 import com.android.contacts.util.Constants;
@@ -246,4 +247,14 @@
             mParent.finish();
         }
     }
+
+    @Override
+    public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
+            boolean globalSearch) {
+        if (globalSearch) {
+            super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
+        } else {
+            ContactsSearchManager.startSearch(this, initialQuery);
+        }
+    }
 }