Merge "Don't use deprecated PagerAdapter methods"
diff --git a/res/layout-sw580dp/join_contact_picker.xml b/res/layout-sw580dp/join_contact_picker.xml
new file mode 100644
index 0000000..3d9127b
--- /dev/null
+++ b/res/layout-sw580dp/join_contact_picker.xml
@@ -0,0 +1,38 @@
+<?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.
+-->
+
+<view xmlns:android="http://schemas.android.com/apk/res/android"
+    class="com.android.contacts.widget.FullHeightLinearLayout"
+    style="@style/ContactPickerLayout"
+    android:orientation="vertical">
+    <!-- See also comments in contact_picker.xml -->
+    <view
+        class="android.widget.SearchView"
+        android:id="@+id/search_view"
+        android:layout_width="match_parent"
+        android:maxWidth="@dimen/contact_picker_search_view_max_width"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="0dip"
+        android:layout_marginRight="@dimen/list_visible_scrollbar_padding"
+        android:paddingRight="0dip"
+        android:iconifiedByDefault="false" />
+
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dip"
+        android:layout_weight="1"
+        android:id="@+id/list_container" />
+</view>
diff --git a/res/layout/join_contact_picker.xml b/res/layout/join_contact_picker.xml
index ea0deaf..ee30525 100644
--- a/res/layout/join_contact_picker.xml
+++ b/res/layout/join_contact_picker.xml
@@ -14,23 +14,13 @@
      limitations under the License.
 -->
 
-<view
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<view xmlns:android="http://schemas.android.com/apk/res/android"
     class="com.android.contacts.widget.FullHeightLinearLayout"
     style="@style/ContactPickerLayout"
     android:orientation="vertical">
-
     <FrameLayout
         android:layout_width="match_parent"
         android:layout_height="0dip"
         android:layout_weight="1"
         android:id="@+id/list_container" />
-
-    <View
-        android:id="@+id/divider"
-        android:layout_width="match_parent"
-        android:layout_height="1dip"
-        android:layout_marginLeft="?attr/contact_browser_list_padding_left"
-        android:layout_marginRight="?attr/contact_browser_list_padding_right"
-        android:background="?android:attr/dividerHorizontal" />
 </view>
diff --git a/res/values-sw580dp/styles.xml b/res/values-sw580dp/styles.xml
index bf5b137..70e2a3b 100644
--- a/res/values-sw580dp/styles.xml
+++ b/res/values-sw580dp/styles.xml
@@ -95,6 +95,8 @@
     <style name="JoinContactActivityTheme" parent="ContactPickerTheme" >
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">match_parent</item>
+        <!-- In the contact picker screen we're using adjustResize but we don't want it here. -->
+        <item name="android:windowSoftInputMode">adjustUnspecified</item>
     </style>
 
     <style name="ContactListFilterTheme" parent="@android:Theme.Holo.Light.Dialog">
diff --git a/src/com/android/contacts/activities/ContactSelectionActivity.java b/src/com/android/contacts/activities/ContactSelectionActivity.java
index b5b6c1d..4b59d34 100644
--- a/src/com/android/contacts/activities/ContactSelectionActivity.java
+++ b/src/com/android/contacts/activities/ContactSelectionActivity.java
@@ -107,7 +107,7 @@
         if (savedState != null) {
             mActionCode = savedState.getInt(KEY_ACTION_CODE);
         }
-
+        
         // Extract relevant information from the intent
         mRequest = mIntentResolver.resolveIntent(getIntent());
         if (!mRequest.isValid()) {
diff --git a/src/com/android/contacts/activities/JoinContactActivity.java b/src/com/android/contacts/activities/JoinContactActivity.java
index 75a13d0..4a277cb 100644
--- a/src/com/android/contacts/activities/JoinContactActivity.java
+++ b/src/com/android/contacts/activities/JoinContactActivity.java
@@ -24,18 +24,29 @@
 import com.android.contacts.list.OnContactPickerActionListener;
 
 import android.app.ActionBar;
+import android.app.ActionBar.LayoutParams;
 import android.app.Fragment;
+import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.ContactsContract;
+import android.text.TextUtils;
 import android.util.Log;
+import android.view.LayoutInflater;
 import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnFocusChangeListener;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.SearchView;
+import android.widget.SearchView.OnCloseListener;
+import android.widget.SearchView.OnQueryTextListener;
 
 /**
  * An activity that shows a list of contacts that can be joined with the target contact.
  */
-public class JoinContactActivity extends ContactsActivity {
+public class JoinContactActivity extends ContactsActivity
+        implements OnQueryTextListener, OnCloseListener, OnFocusChangeListener {
 
     private static final String TAG = "JoinContactActivity";
 
@@ -59,6 +70,7 @@
     private long mTargetContactId;
 
     private JoinContactListFragment mListFragment;
+    private SearchView mSearchView;
 
     @Override
     public void onAttachFragment(Fragment fragment) {
@@ -93,12 +105,7 @@
                     .commitAllowingStateLoss();
         }
 
-        final ActionBar actionBar = getActionBar();
-        if (actionBar != null) {
-            actionBar.setDisplayShowHomeEnabled(true);
-            actionBar.setDisplayHomeAsUpEnabled(true);
-            actionBar.setDisplayShowTitleEnabled(true);
-        }
+        prepareSearchViewAndActionBar();
     }
 
     private void setupActionListener() {
@@ -125,6 +132,74 @@
         });
     }
 
+    private void prepareSearchViewAndActionBar() {
+        final ActionBar actionBar = getActionBar();
+        if (actionBar != null) {
+            final View searchViewOnLayout = findViewById(R.id.search_view);
+            if (searchViewOnLayout != null) {
+                searchViewOnLayout.setVisibility(View.GONE);
+            }
+
+            final View searchViewLayout = LayoutInflater.from(actionBar.getThemedContext())
+                    .inflate(R.layout.custom_action_bar, null);
+            mSearchView = (SearchView) searchViewLayout.findViewById(R.id.search_view);
+
+            // In order to make the SearchView look like "shown via search menu", we need to
+            // manually setup its state. See also DialtactsActivity.java and ActionBarAdapter.java.
+            mSearchView.setIconifiedByDefault(true);
+            mSearchView.setQueryHint(getString(R.string.hint_findContacts));
+            mSearchView.setIconified(false);
+
+            mSearchView.setOnQueryTextListener(this);
+            mSearchView.setOnCloseListener(this);
+            mSearchView.setOnQueryTextFocusChangeListener(this);
+
+            actionBar.setCustomView(searchViewLayout,
+                    new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+            actionBar.setDisplayShowCustomEnabled(true);
+            actionBar.setDisplayShowHomeEnabled(true);
+            actionBar.setDisplayHomeAsUpEnabled(true);
+        } else {
+            mSearchView = (SearchView) findViewById(R.id.search_view);
+            mSearchView.setQueryHint(getString(R.string.hint_findContacts));
+            mSearchView.setOnQueryTextListener(this);
+            mSearchView.setOnQueryTextFocusChangeListener(this);
+        }
+
+        // Clear focus and suppress keyboard show-up.
+        mSearchView.clearFocus();
+    }
+
+    @Override
+    public boolean onQueryTextChange(String newText) {
+        mListFragment.setQueryString(newText, true);
+        return false;
+    }
+
+    @Override
+    public boolean onQueryTextSubmit(String query) {
+        return false;
+    }
+
+    @Override
+    public boolean onClose() {
+        if (!TextUtils.isEmpty(mSearchView.getQuery())) {
+            mSearchView.setQuery(null, true);
+        }
+        return true;
+    }
+
+    @Override
+    public void onFocusChange(View view, boolean hasFocus) {
+        switch (view.getId()) {
+            case R.id.search_view: {
+                if (hasFocus) {
+                    showInputMethod(mSearchView.findFocus());
+                }
+            }
+        }
+    }
+
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         switch (item.getItemId()) {
@@ -156,4 +231,14 @@
             mListFragment.onPickerResult(data);
         }
     }
+
+    private void showInputMethod(View view) {
+        final InputMethodManager imm = (InputMethodManager)
+                getSystemService(Context.INPUT_METHOD_SERVICE);
+        if (imm != null) {
+            if (!imm.showSoftInput(view, 0)) {
+                Log.w(TAG, "Failed to show soft input method.");
+            }
+        }
+    }
 }
diff --git a/src/com/android/contacts/list/ContactEntryListAdapter.java b/src/com/android/contacts/list/ContactEntryListAdapter.java
index cf86dc9..d95a1bc 100644
--- a/src/com/android/contacts/list/ContactEntryListAdapter.java
+++ b/src/com/android/contacts/list/ContactEntryListAdapter.java
@@ -131,6 +131,28 @@
         return partition;
     }
 
+    /**
+     * Remove all directories after the default directory. This is typically used when contacts
+     * list screens are asked to exit the search mode and thus need to remove all remote directory
+     * results for the search.
+     *
+     * This code assumes that the default directory and directories before that should not be
+     * deleted (e.g. Join screen has "suggested contacts" directory before the default director,
+     * and we should not remove the directory).
+     */
+    /* package */ void removeDirectoriesAfterDefault() {
+        final int partitionCount = getPartitionCount();
+        for (int i = partitionCount - 1; i >= 0; i--) {
+            final Partition partition = getPartition(i);
+            if ((partition instanceof DirectoryPartition)
+                    && ((DirectoryPartition) partition).getDirectoryId() == Directory.DEFAULT) {
+                break;
+            } else {
+                removePartition(i);
+            }
+        }
+    }
+
     private int getPartitionByDirectoryId(long id) {
         int count = getPartitionCount();
         for (int i = 0; i < count; i++) {
diff --git a/src/com/android/contacts/list/ContactEntryListFragment.java b/src/com/android/contacts/list/ContactEntryListFragment.java
index 8db477e..5ee25a2 100644
--- a/src/com/android/contacts/list/ContactEntryListFragment.java
+++ b/src/com/android/contacts/list/ContactEntryListFragment.java
@@ -613,12 +613,10 @@
 
                 mAdapter.clearPartitions();
                 if (!flag) {
-                    // If we are switching from search to regular display,
-                    // remove all directory partitions (except the default one).
-                    int count = mAdapter.getPartitionCount();
-                    for (int i = count; --i >= 1;) {
-                        mAdapter.removePartition(i);
-                    }
+                    // If we are switching from search to regular display, remove all directory
+                    // partitions after default one, assuming they are remote directories which
+                    // should be cleaned up on exiting the search mode.
+                    mAdapter.removeDirectoriesAfterDefault();
                 }
                 mAdapter.configureDefaultPartition(false, flag);
             }
diff --git a/src/com/android/contacts/list/JoinContactListAdapter.java b/src/com/android/contacts/list/JoinContactListAdapter.java
index bfe8c53..80ddc83 100644
--- a/src/com/android/contacts/list/JoinContactListAdapter.java
+++ b/src/com/android/contacts/list/JoinContactListAdapter.java
@@ -20,7 +20,6 @@
 import android.content.Context;
 import android.content.CursorLoader;
 import android.database.Cursor;
-import android.database.MatrixCursor;
 import android.net.Uri;
 import android.net.Uri.Builder;
 import android.provider.ContactsContract;
@@ -53,7 +52,6 @@
 
     @Override
     protected void addPartitions() {
-
         // Partition 0: suggestions
         addPartition(false, true);
 
@@ -69,11 +67,11 @@
     public void configureLoader(CursorLoader cursorLoader, long directoryId) {
         JoinContactLoader loader = (JoinContactLoader) cursorLoader;
 
-        Builder builder = Contacts.CONTENT_URI.buildUpon();
+        final Builder builder = Contacts.CONTENT_URI.buildUpon();
         builder.appendEncodedPath(String.valueOf(mTargetContactId));
         builder.appendEncodedPath(AggregationSuggestions.CONTENT_DIRECTORY);
 
-        String filter = getQueryString();
+        final String filter = getQueryString();
         if (!TextUtils.isEmpty(filter)) {
             builder.appendEncodedPath(Uri.encode(filter));
         }
@@ -84,13 +82,22 @@
 
         // TODO simplify projection
         loader.setProjection(getProjection(false));
-        Uri allContactsUri = buildSectionIndexerUri(Contacts.CONTENT_URI).buildUpon()
+        final Uri allContactsUri;
+        if (!TextUtils.isEmpty(filter)) {
+            allContactsUri = buildSectionIndexerUri(Contacts.CONTENT_FILTER_URI).buildUpon()
+                .appendEncodedPath(Uri.encode(filter))
                 .appendQueryParameter(
                         ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(Directory.DEFAULT))
                 .build();
+        } else {
+            allContactsUri = buildSectionIndexerUri(Contacts.CONTENT_URI).buildUpon()
+                .appendQueryParameter(
+                        ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(Directory.DEFAULT))
+                .build();
+        }
         loader.setUri(allContactsUri);
         loader.setSelection(Contacts._ID + "!=?");
-        loader.setSelectionArgs(new String[]{String.valueOf(mTargetContactId)});
+        loader.setSelectionArgs(new String[]{ String.valueOf(mTargetContactId) });
         if (getSortOrder() == ContactsContract.Preferences.SORT_ORDER_PRIMARY) {
             loader.setSortOrder(Contacts.SORT_KEY_PRIMARY);
         } else {
@@ -120,7 +127,7 @@
 
     @Override
     public int getViewTypeCount() {
-        return super.getViewTypeCount() + 1;
+        return super.getViewTypeCount();
     }
 
     @Override
@@ -173,14 +180,14 @@
     protected void bindView(View itemView, int partition, Cursor cursor, int position) {
         switch (partition) {
             case PARTITION_SUGGESTIONS: {
-                final ContactListItemView view = (ContactListItemView)itemView;
+                final ContactListItemView view = (ContactListItemView) itemView;
                 view.setSectionHeader(null);
                 bindPhoto(view, partition, cursor);
                 bindName(view, cursor);
                 break;
             }
             case PARTITION_ALL_CONTACTS: {
-                final ContactListItemView view = (ContactListItemView)itemView;
+                final ContactListItemView view = (ContactListItemView) itemView;
                 bindSectionHeaderAndDivider(view, position, cursor);
                 bindPhoto(view, partition, cursor);
                 bindName(view, cursor);
diff --git a/src/com/android/contacts/list/JoinContactListFragment.java b/src/com/android/contacts/list/JoinContactListFragment.java
index 5b27bdf..7c6767b 100644
--- a/src/com/android/contacts/list/JoinContactListFragment.java
+++ b/src/com/android/contacts/list/JoinContactListFragment.java
@@ -26,6 +26,7 @@
 import android.database.Cursor;
 import android.os.Bundle;
 import android.provider.ContactsContract.Contacts;
+import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -103,7 +104,10 @@
         configureAdapter();
 
         getLoaderManager().initLoader(DISPLAY_NAME_LOADER, null, mLoaderCallbacks);
-        getLoaderManager().initLoader(JoinContactListAdapter.PARTITION_ALL_CONTACTS,
+
+        // When this method is called, Uri to be used may be changed. We should use restartLoader()
+        // to load the parameter again.
+        getLoaderManager().restartLoader(JoinContactListAdapter.PARTITION_ALL_CONTACTS,
                 null, mLoaderCallbacks);
     }
 
@@ -167,4 +171,11 @@
             mTargetContactId = savedState.getLong(KEY_TARGET_CONTACT_ID);
         }
     }
+
+    @Override
+    public void setQueryString(String queryString, boolean delaySelection) {
+        super.setQueryString(queryString, delaySelection);
+
+        setSearchMode(!TextUtils.isEmpty(queryString));
+    }
 }
diff --git a/src/com/android/contacts/list/JoinContactLoader.java b/src/com/android/contacts/list/JoinContactLoader.java
index 2f1f9b0..c43560e 100644
--- a/src/com/android/contacts/list/JoinContactLoader.java
+++ b/src/com/android/contacts/list/JoinContactLoader.java
@@ -20,6 +20,7 @@
 import android.database.Cursor;
 import android.database.MatrixCursor;
 import android.net.Uri;
+import android.util.Log;
 
 /**
  * A specialized loader for the Join Contacts UI.  It executes two queries:
@@ -29,7 +30,7 @@
 
     private String[] mProjection;
     private Uri mSuggestionUri;
-    private MatrixCursor mSuggestionsCursor;
+    private Cursor mSuggestionsCursor;
 
     public JoinContactLoader(Context context) {
         super(context, null, null, null, null, null);
@@ -53,28 +54,8 @@
     public Cursor loadInBackground() {
         // First execute the suggestions query, then call super.loadInBackground
         // to load the entire list
-        mSuggestionsCursor = loadSuggestions();
+        mSuggestionsCursor = getContext().getContentResolver()
+                .query(mSuggestionUri, mProjection, null, null, null);
         return super.loadInBackground();
     }
-
-    /**
-     * Loads join suggestions into a MatrixCursor.
-     */
-    private MatrixCursor loadSuggestions() {
-        Cursor cursor = getContext().getContentResolver().query(mSuggestionUri, mProjection,
-                null, null, null);
-        try {
-            MatrixCursor matrix = new MatrixCursor(mProjection);
-            Object[] row = new Object[mProjection.length];
-            while (cursor.moveToNext()) {
-                for (int i = 0; i < row.length; i++) {
-                    row[i] = cursor.getString(i);
-                }
-                matrix.addRow(row);
-            }
-            return matrix;
-        } finally {
-            cursor.close();
-        }
-    }
 }
\ No newline at end of file