Merge "Add multiselect to contact picker"
diff --git a/res/layout/contact_picker.xml b/res/layout/contact_picker.xml
index 2efe471..faf5563 100644
--- a/res/layout/contact_picker.xml
+++ b/res/layout/contact_picker.xml
@@ -14,9 +14,14 @@
      limitations under the License.
 -->
 
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    style="@style/ContactPickerLayout"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/list_container"
+    android:orientation="vertical"
     android:layout_width="match_parent"
-    android:layout_height="match_parent" />
+    android:layout_height="match_parent" >
+
+    <include
+        layout="@layout/people_activity_toolbar"
+        android:id="@+id/toolbar_parent" />
+
+</LinearLayout>
diff --git a/res/layout/custom_action_bar.xml b/res/layout/custom_action_bar.xml
deleted file mode 100644
index 5b930ea..0000000
--- a/res/layout/custom_action_bar.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 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.
--->
-
-<!-- Dimensions are set at runtime in ActionBarAdapter -->
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="0dip"
-    android:layout_height="0dip" >
-
-    <!-- To prevent the search view from getting the initial focus.  -->
-    <View
-        android:focusable="true"
-        android:focusableInTouchMode="true"
-        android:layout_width="1px"
-        android:layout_height="1px" >
-        <requestFocus />
-    </View>
-    <SearchView
-        android:id="@+id/search_view"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:iconifiedByDefault="false"
-        android:inputType="textFilter"
-        android:theme="@style/ContactPickerSearchTheme" />
-
-</FrameLayout>
diff --git a/res/menu/group_member_picker.xml b/res/menu/group_member_picker.xml
new file mode 100644
index 0000000..ab1fadf
--- /dev/null
+++ b/res/menu/group_member_picker.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:contacts="http://schemas.android.com/apk/res-auto">
+
+    <item
+        android:id="@+id/menu_done"
+        android:icon="@drawable/ic_done_wht_24dp"
+        android:title="@string/menu_addContactsToGroup"
+        contacts:showAsAction="always" />
+
+    <item
+        android:id="@+id/menu_select"
+        android:title="@string/menu_selectForGroup" />
+        contacts:showAsAction="never" />
+
+</menu>
diff --git a/res/menu/search_menu.xml b/res/menu/search_menu.xml
index 355442e..89068d0 100644
--- a/res/menu/search_menu.xml
+++ b/res/menu/search_menu.xml
@@ -13,10 +13,11 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:contacts="http://schemas.android.com/apk/res-auto">
     <item
         android:id="@+id/menu_search"
         android:icon="@drawable/ic_ab_search"
         android:title="@string/menu_search"
-        android:showAsAction="always" />
+        contacts:showAsAction="always" />
 </menu>
diff --git a/res/values-sw600dp/styles.xml b/res/values-sw600dp/styles.xml
index fb0f483..11dc54e 100644
--- a/res/values-sw600dp/styles.xml
+++ b/res/values-sw600dp/styles.xml
@@ -15,11 +15,6 @@
 -->
 <resources>
 
-    <style name="ContactPickerLayout" parent="ContactPickerTheme">
-        <item name="android:layout_width">match_parent</item>
-        <item name="android:layout_height">match_parent</item>
-    </style>
-
     <style name="DetailActivityTheme" parent="@android:Theme.Dialog">
         <item name="android:windowContentOverlay">@null</item>
     </style>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 479afd2..e735c58 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -139,6 +139,12 @@
     <!-- Menu item to search for contacts to add to the currently selected label. CHAR LIMIT=30] -->
     <string name="menu_addToGroup">Add contact</string>
 
+    <!-- Menu item to select multiple contacts to add to the currently selected label. CHAR LIMIT=30] -->
+    <string name="menu_selectForGroup">Select contacts</string>
+
+    <!-- Menu item to add selected contacts to the currently selected label. CHAR LIMIT=30] -->
+    <string name="menu_addContactsToGroup">Add contacts</string>
+
     <!-- Menu item to remove the currently selected contacts from the currently selected label. [CHAR LIMIT=60] -->
     <string name="menu_removeFromGroup">Remove from label</string>
 
@@ -334,8 +340,8 @@
     <!-- Toast displayed when contacts are removed from a label. [CHAR LIMIT=50] -->
     <string name="groupMembersRemovedToast">Removed contacts</string>
 
-    <!-- Toast displayed when a contact is added to a label. [CHAR LIMIT=50] -->
-    <string name="groupMembersAddedToast">Added contact</string>
+    <!-- Toast displayed when one or more contacts is added to a label. [CHAR LIMIT=50] -->
+    <string name="groupMembersAddedToast">Added to label</string>
 
     <!-- Toast displayed when saving a label failed [CHAR LIMIT=70] -->
     <string name="groupSavedErrorToast">Couldn\'t save label changes.</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 0358caa..5e46c86 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -221,18 +221,6 @@
         <item name="titleTextAppearance">@style/ContactsActionBarTitleTextAppCompat</item>
     </style>
 
-    <style name="ContactsPickerActionBarStyle" parent="@style/ContactsActionBarStyle">
-        <!-- when first loading, don't show title or up button -->
-        <item name="android:displayOptions"></item>
-    </style>
-
-    <style name="ContactPickerSearchTheme" parent="@style/PeopleTheme">
-        <item name="android:textColorPrimary">@android:color/white</item>
-        <item name="android:textColorHint">?android:textColorHintInverse</item>
-        <item name="android:colorControlActivated">?android:textColorHintInverse</item>
-        <item name="android:colorControlNormal">@android:color/white</item>
-    </style>
-
     <!-- Text in the action bar at the top of the screen -->
     <style name="ContactsActionBarTitleText"
            parent="@android:style/TextAppearance.Material.Widget.ActionBar.Title">
@@ -276,13 +264,7 @@
         <item name="android:listSelector">?android:attr/listChoiceBackgroundIndicator</item>
     </style>
 
-    <style name="ContactPickerTheme" parent="@style/PeopleTheme" >
-        <item name="android:actionBarStyle">@style/ContactsPickerActionBarStyle</item>
-    </style>
-
-    <style name="ContactPickerLayout" parent="ContactPickerTheme">
-        <item name="android:layout_width">match_parent</item>
-        <item name="android:layout_height">match_parent</item>
+    <style name="ContactPickerTheme" parent="PeopleActivityTheme" >
     </style>
 
     <style name="ContactsPreferencesTheme" parent="@style/PeopleTheme">
diff --git a/src/com/android/contacts/activities/ActionBarAdapter.java b/src/com/android/contacts/activities/ActionBarAdapter.java
index 3af9c4b..047be6a 100644
--- a/src/com/android/contacts/activities/ActionBarAdapter.java
+++ b/src/com/android/contacts/activities/ActionBarAdapter.java
@@ -81,6 +81,7 @@
 
     private boolean mSelectionMode;
     private boolean mSearchMode;
+    private boolean mTransparentStatuBar;
     private String mQueryString;
 
     private EditText mSearchView;
@@ -314,6 +315,11 @@
         return mSelectionMode;
     }
 
+    /** Whether to make the status bar transparent. */
+    public void setTransparentStatuBar(boolean flag) {
+        mTransparentStatuBar = flag;
+    }
+
     public void setSearchMode(boolean flag) {
         if (mSearchMode != flag) {
             mSearchMode = flag;
@@ -517,6 +523,10 @@
             final int cabStatusBarColor = mActivity.getResources().getColor(
                     R.color.contextual_selection_bar_status_bar_color);
             mActivity.getWindow().setStatusBarColor(cabStatusBarColor);
+        } else if (!mTransparentStatuBar) {
+            final int normalStatusBarColor = ContextCompat.getColor(
+                    mActivity, R.color.primary_color_dark);
+            mActivity.getWindow().setStatusBarColor(normalStatusBarColor);
         } else {
             mActivity.getWindow().setStatusBarColor(Color.TRANSPARENT);
         }
diff --git a/src/com/android/contacts/activities/ContactSelectionActivity.java b/src/com/android/contacts/activities/ContactSelectionActivity.java
index 17ba765..9885a14 100644
--- a/src/com/android/contacts/activities/ContactSelectionActivity.java
+++ b/src/com/android/contacts/activities/ContactSelectionActivity.java
@@ -16,51 +16,46 @@
 
 package com.android.contacts.activities;
 
-import android.app.ActionBar;
-import android.app.ActionBar.LayoutParams;
 import android.app.Fragment;
 import android.content.ActivityNotFoundException;
-import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.ContactsContract.Contacts;
+import android.support.v7.widget.Toolbar;
 import android.text.TextUtils;
 import android.util.Log;
-import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.View.OnFocusChangeListener;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.SearchView;
-import android.widget.SearchView.OnCloseListener;
-import android.widget.SearchView.OnQueryTextListener;
 import android.widget.Toast;
 
-import com.android.contacts.ContactsActivity;
+import com.android.contacts.AppCompatContactsActivity;
 import com.android.contacts.R;
 import com.android.contacts.common.activity.RequestPermissionsActivity;
 import com.android.contacts.common.list.ContactEntryListFragment;
-import com.android.contacts.list.GroupMemberPickerFragment;
-import com.android.contacts.common.model.account.AccountWithDataSet;
+import com.android.contacts.common.list.DirectoryListLoader;
+import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
+import com.android.contacts.common.list.PhoneNumberPickerFragment;
+import com.android.contacts.common.util.ViewUtil;
 import com.android.contacts.editor.EditorIntents;
 import com.android.contacts.list.ContactPickerFragment;
 import com.android.contacts.list.ContactsIntentResolver;
 import com.android.contacts.list.ContactsRequest;
-import com.android.contacts.common.list.DirectoryListLoader;
 import com.android.contacts.list.EmailAddressPickerFragment;
+import com.android.contacts.list.GroupMemberPickerFragment;
 import com.android.contacts.list.JoinContactListFragment;
 import com.android.contacts.list.LegacyPhoneNumberPickerFragment;
+import com.android.contacts.list.MultiSelectContactsListFragment;
+import com.android.contacts.list.MultiSelectContactsListFragment.OnCheckBoxListActionListener;
 import com.android.contacts.list.OnContactPickerActionListener;
 import com.android.contacts.list.OnEmailAddressPickerActionListener;
-import com.android.contacts.list.UiIntentActions;
-import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
 import com.android.contacts.list.OnPostalAddressPickerActionListener;
-import com.android.contacts.common.list.PhoneNumberPickerFragment;
 import com.android.contacts.list.PostalAddressPickerFragment;
+import com.android.contacts.list.UiIntentActions;
 
 import java.util.ArrayList;
 
@@ -68,10 +63,10 @@
  * Displays a list of contacts (or phone numbers or postal addresses) for the
  * purposes of selecting one.
  */
-public class ContactSelectionActivity extends ContactsActivity
-        implements View.OnCreateContextMenuListener, OnQueryTextListener, OnClickListener,
-                OnCloseListener, OnFocusChangeListener {
-    private static final String TAG = "ContactSelectionActivity";
+public class ContactSelectionActivity extends AppCompatContactsActivity implements
+        View.OnCreateContextMenuListener, ActionBarAdapter.Listener, OnClickListener,
+        OnFocusChangeListener, OnCheckBoxListActionListener {
+    private static final String TAG = "ContactSelection";
 
     private static final String KEY_ACTION_CODE = "actionCode";
     private static final String KEY_SEARCH_MODE = "searchMode";
@@ -85,8 +80,9 @@
     private boolean mIsSearchSupported;
 
     private ContactsRequest mRequest;
-    private SearchView mSearchView;
-    private View mSearchViewContainer;
+
+    private ActionBarAdapter mActionBarAdapter;
+    private Toolbar mToolbar;
 
     public ContactSelectionActivity() {
         mIntentResolver = new ContactsIntentResolver(this);
@@ -121,8 +117,6 @@
             return;
         }
 
-        configureActivityTitle();
-
         setContentView(R.layout.contact_picker);
 
         if (mActionCode != mRequest.getActionCode()) {
@@ -130,63 +124,46 @@
             configureListFragment();
         }
 
-        prepareSearchViewAndActionBar();
+        prepareSearchViewAndActionBar(savedState);
+        configureActivityTitle();
     }
 
-    private void prepareSearchViewAndActionBar() {
-        final ActionBar actionBar = getActionBar();
-        mSearchViewContainer = LayoutInflater.from(actionBar.getThemedContext())
-                .inflate(R.layout.custom_action_bar, null);
-        mSearchView = (SearchView) mSearchViewContainer.findViewById(R.id.search_view);
+    public boolean isSelectionMode() {
+        return mActionBarAdapter.isSelectionMode();
+    }
+
+    public boolean isSearchMode() {
+        return mActionBarAdapter.isSearchMode();
+    }
+
+    private void prepareSearchViewAndActionBar(Bundle savedState) {
+        mToolbar = getView(R.id.toolbar);
+        setSupportActionBar(mToolbar);
+
+        // Add a shadow under the toolbar.
+        ViewUtil.addRectangularOutlineProvider(findViewById(R.id.toolbar_parent), getResources());
+
+        mActionBarAdapter = new ActionBarAdapter(this, this, getSupportActionBar(),
+                /* portraitTabs */ null, /* landscapeTabs */ null, mToolbar,
+                R.string.enter_contact_name);
+        mActionBarAdapter.setShowHomeIcon(true);
+        mActionBarAdapter.setShowHomeAsUp(true);
+        mActionBarAdapter.setTransparentStatuBar(false);
+        mActionBarAdapter.initialize(savedState, mRequest);
 
         // Postal address pickers (and legacy pickers) don't support search, so just show
         // "HomeAsUp" button and title.
         if (mRequest.getActionCode() == ContactsRequest.ACTION_PICK_POSTAL ||
                 mRequest.isLegacyCompatibilityMode()) {
-            mSearchView.setVisibility(View.GONE);
-            if (actionBar != null) {
-                actionBar.setDisplayShowHomeEnabled(true);
-                actionBar.setDisplayHomeAsUpEnabled(true);
-                actionBar.setDisplayShowTitleEnabled(true);
-            }
             mIsSearchSupported = false;
-            configureSearchMode();
-            return;
+        } else {
+            mIsSearchSupported = true;
         }
-
-        actionBar.setDisplayShowHomeEnabled(true);
-        actionBar.setDisplayHomeAsUpEnabled(true);
-
-        // 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.setFocusable(true);
-
-        mSearchView.setOnQueryTextListener(this);
-        mSearchView.setOnCloseListener(this);
-        mSearchView.setOnQueryTextFocusChangeListener(this);
-
-        actionBar.setCustomView(mSearchViewContainer,
-                new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
-        actionBar.setDisplayShowCustomEnabled(true);
-
-        mIsSearchSupported = true;
         configureSearchMode();
     }
 
     private void configureSearchMode() {
-        final ActionBar actionBar = getActionBar();
-        if (mIsSearchMode) {
-            actionBar.setDisplayShowTitleEnabled(false);
-            mSearchViewContainer.setVisibility(View.VISIBLE);
-            mSearchView.requestFocus();
-        } else {
-            actionBar.setDisplayShowTitleEnabled(true);
-            mSearchViewContainer.setVisibility(View.GONE);
-            mSearchView.setQuery(null, true);
-        }
+        mActionBarAdapter.setSearchMode(mIsSearchMode);
         invalidateOptionsMenu();
     }
 
@@ -215,67 +192,60 @@
 
     private void configureActivityTitle() {
         if (!TextUtils.isEmpty(mRequest.getActivityTitle())) {
-            setTitle(mRequest.getActivityTitle());
+            getSupportActionBar().setTitle(mRequest.getActivityTitle());
             return;
         }
-
+        int titleResId = -1;
         int actionCode = mRequest.getActionCode();
         switch (actionCode) {
             case ContactsRequest.ACTION_INSERT_OR_EDIT_CONTACT: {
-                setTitle(R.string.contactInsertOrEditActivityTitle);
+                titleResId = R.string.contactInsertOrEditActivityTitle;
                 break;
             }
-
             case ContactsRequest.ACTION_PICK_CONTACT: {
-                setTitle(R.string.contactPickerActivityTitle);
+                titleResId = R.string.contactPickerActivityTitle;
                 break;
             }
-
             case ContactsRequest.ACTION_PICK_OR_CREATE_CONTACT: {
-                setTitle(R.string.contactPickerActivityTitle);
+                titleResId = R.string.contactPickerActivityTitle;
                 break;
             }
-
             case ContactsRequest.ACTION_CREATE_SHORTCUT_CONTACT: {
-                setTitle(R.string.shortcutActivityTitle);
+                titleResId = R.string.shortcutActivityTitle;
                 break;
             }
-
             case ContactsRequest.ACTION_PICK_PHONE: {
-                setTitle(R.string.contactPickerActivityTitle);
+                titleResId = R.string.contactPickerActivityTitle;
                 break;
             }
-
             case ContactsRequest.ACTION_PICK_EMAIL: {
-                setTitle(R.string.contactPickerActivityTitle);
+                titleResId = R.string.contactPickerActivityTitle;
                 break;
             }
-
             case ContactsRequest.ACTION_CREATE_SHORTCUT_CALL: {
-                setTitle(R.string.callShortcutActivityTitle);
+                titleResId = R.string.callShortcutActivityTitle;
                 break;
             }
-
             case ContactsRequest.ACTION_CREATE_SHORTCUT_SMS: {
-                setTitle(R.string.messageShortcutActivityTitle);
+                titleResId = R.string.messageShortcutActivityTitle;
                 break;
             }
-
             case ContactsRequest.ACTION_PICK_POSTAL: {
-                setTitle(R.string.contactPickerActivityTitle);
+                titleResId = R.string.contactPickerActivityTitle;
                 break;
             }
-
             case ContactsRequest.ACTION_PICK_JOIN: {
-                setTitle(R.string.titleJoinContactDataWith);
+                titleResId = R.string.titleJoinContactDataWith;
                 break;
             }
-
             case ContactsRequest.ACTION_PICK_GROUP_MEMBERS: {
-                setTitle(R.string.contactPickerActivityTitle);
+                titleResId = R.string.contactPickerActivityTitle;
                 break;
             }
         }
+        if (titleResId > 0) {
+            getSupportActionBar().setTitle(titleResId);
+        }
     }
 
     /**
@@ -411,11 +381,75 @@
         } else if (mListFragment instanceof GroupMemberPickerFragment) {
             ((GroupMemberPickerFragment) mListFragment).setListener(
                     new GroupMemberPickerListener());
+            getMultiSelectListFragment().setCheckBoxListListener(this);
         } else {
             throw new IllegalStateException("Unsupported list fragment type: " + mListFragment);
         }
     }
 
+    private MultiSelectContactsListFragment getMultiSelectListFragment() {
+        if (mListFragment instanceof MultiSelectContactsListFragment) {
+            return (MultiSelectContactsListFragment) mListFragment;
+        }
+        return null;
+    }
+
+    @Override
+    public void onAction(int action) {
+        switch (action) {
+            case ActionBarAdapter.Listener.Action.START_SEARCH_MODE:
+                mIsSearchMode = true;
+                configureSearchMode();
+                break;
+            case ActionBarAdapter.Listener.Action.CHANGE_SEARCH_QUERY:
+                final String queryString = mActionBarAdapter.getQueryString();
+                mListFragment.setQueryString(queryString, /* delaySelection */ false);
+                break;
+            case ActionBarAdapter.Listener.Action.START_SELECTION_MODE:
+                if (getMultiSelectListFragment() != null) {
+                    getMultiSelectListFragment().displayCheckBoxes(true);
+                }
+                invalidateOptionsMenu();
+                break;
+            case ActionBarAdapter.Listener.Action.STOP_SEARCH_AND_SELECTION_MODE:
+                mActionBarAdapter.setSearchMode(false);
+                if (getMultiSelectListFragment() != null) {
+                    getMultiSelectListFragment().displayCheckBoxes(false);
+                }
+                invalidateOptionsMenu();
+                break;
+        }
+    }
+
+    @Override
+    public void onSelectedTabChanged() {
+    }
+
+    @Override
+    public void onUpButtonPressed() {
+        onBackPressed();
+    }
+
+    @Override
+    public void onStartDisplayingCheckBoxes() {
+        mActionBarAdapter.setSelectionMode(true);
+    }
+
+    @Override
+    public void onSelectedContactIdsChanged() {
+        if (mListFragment instanceof MultiSelectContactsListFragment) {
+            mActionBarAdapter.setSelectionCount(((MultiSelectContactsListFragment) mListFragment)
+                    .getSelectedContactIds().size());
+            // Show or hide the multi select "Done" button
+            invalidateOptionsMenu();
+        }
+    }
+
+    @Override
+    public void onStopDisplayingCheckBoxes() {
+        mActionBarAdapter.setSelectionMode(false);
+    }
+
     private final class ContactPickerActionListener implements OnContactPickerActionListener {
         @Override
         public void onCreateNewContactAction() {
@@ -492,6 +526,18 @@
             intent.putExtra(UiIntentActions.TARGET_CONTACT_ID_EXTRA_KEY, contactId);
             returnPickerResult(intent);
         }
+
+        @Override
+        public void onGroupMembersSelected(long[] contactIds) {
+            final Intent intent = new Intent();
+            intent.putExtra(UiIntentActions.TARGET_CONTACT_IDS_EXTRA_KEY, contactIds);
+            returnPickerResult(intent);
+        }
+
+        @Override
+        public void onSelectGroupMembers() {
+            mActionBarAdapter.setSelectionMode(true);
+        }
     }
 
     private final class PostalAddressPickerActionListener implements
@@ -529,30 +575,11 @@
     }
 
     @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());
+                    mActionBarAdapter.setFocusOnSearchView();
                 }
             }
         }
@@ -600,16 +627,6 @@
         startActivityAndForwardResult(intent);
     }
 
-    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.");
-            }
-        }
-    }
-
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         super.onCreateOptionsMenu(menu);
@@ -631,6 +648,8 @@
         if (mIsSearchMode) {
             mIsSearchMode = false;
             configureSearchMode();
+        } else if (isSelectionMode()) {
+            mActionBarAdapter.setSelectionMode(false);
         } else {
             super.onBackPressed();
         }
diff --git a/src/com/android/contacts/activities/GroupMembersActivity.java b/src/com/android/contacts/activities/GroupMembersActivity.java
index 2c734d8..e6e838b 100644
--- a/src/com/android/contacts/activities/GroupMembersActivity.java
+++ b/src/com/android/contacts/activities/GroupMembersActivity.java
@@ -91,15 +91,15 @@
     private static class AddGroupMembersAsyncTask extends AsyncTask<Void, Void, Intent> {
 
         private final Context mContext;
-        private final long mContactId;
+        private final long[] mContactIds;
         private final long mGroupId;
         private final String mAccountName;
         private final String mAccountType;
 
-        AddGroupMembersAsyncTask(Context context, long contactId, long groupId, String accountName,
-                String accountType) {
+        AddGroupMembersAsyncTask(Context context, long[] contactIds, long groupId,
+                String accountName, String accountType) {
             mContext = context;
-            mContactId = contactId;
+            mContactIds = contactIds;
             mGroupId = groupId;
             mAccountName = accountName;
             mAccountType = accountType;
@@ -125,11 +125,17 @@
                     .appendQueryParameter(RawContacts.ACCOUNT_TYPE, mAccountType)
                     .build();
             final String[] projection = new String[]{RawContacts._ID};
-            final String selection = RawContacts.CONTACT_ID + "=?";
-            final String[] selectionArgs = new String[1];
-            selectionArgs[0] = Long.toString(mContactId);
+            final StringBuilder selection = new StringBuilder();
+            final String[] selectionArgs = new String[mContactIds.length];
+            for (int i = 0; i < mContactIds.length; i++) {
+                if (i > 0) {
+                    selection.append(" OR ");
+                }
+                selection.append(RawContacts.CONTACT_ID).append("=?");
+                selectionArgs[i] = Long.toString(mContactIds[i]);
+            }
             final Cursor cursor = mContext.getContentResolver().query(
-                    rawContactUri, projection, selection, selectionArgs, null, null);
+                    rawContactUri, projection, selection.toString(), selectionArgs, null, null);
             final long[] rawContactIds = new long[cursor.getCount()];
             try {
                 int i = 0;
@@ -478,9 +484,17 @@
     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         if (requestCode == RESULT_GROUP_ADD_MEMBER && resultCode == RESULT_OK && data != null) {
-            final long contactId = data.getLongExtra(
-                    UiIntentActions.TARGET_CONTACT_ID_EXTRA_KEY, -1);
-            new AddGroupMembersAsyncTask(this, contactId, mGroupMetadata.groupId,
+            long[] contactIds = data.getLongArrayExtra(
+                    UiIntentActions.TARGET_CONTACT_IDS_EXTRA_KEY);
+            if (contactIds == null) {
+                final long contactId = data.getLongExtra(
+                        UiIntentActions.TARGET_CONTACT_ID_EXTRA_KEY, -1);
+                if (contactId > -1) {
+                    contactIds = new long[1];
+                    contactIds[0] = contactId;
+                }
+            }
+            new AddGroupMembersAsyncTask(this, contactIds, mGroupMetadata.groupId,
                     mGroupMetadata.accountName, mGroupMetadata.accountType)
                     .execute();
         }
diff --git a/src/com/android/contacts/list/GroupMemberPickerFragment.java b/src/com/android/contacts/list/GroupMemberPickerFragment.java
index d7ad9ed..a90ccbe 100644
--- a/src/com/android/contacts/list/GroupMemberPickerFragment.java
+++ b/src/com/android/contacts/list/GroupMemberPickerFragment.java
@@ -15,17 +15,21 @@
  */
 package com.android.contacts.list;
 
+import android.app.Activity;
 import android.content.Loader;
 import android.database.Cursor;
 import android.database.CursorWrapper;
 import android.os.Bundle;
 import android.util.Log;
 import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
 
+import com.android.contacts.activities.ContactSelectionActivity;
 import com.android.contacts.common.R;
-import com.android.contacts.common.list.ContactEntryListFragment;
 import com.android.contacts.common.list.ContactListAdapter.ContactQuery;
 import com.android.contacts.common.list.ContactListFilter;
 import com.android.contacts.common.list.DefaultContactListAdapter;
@@ -36,7 +40,7 @@
  * Fragment containing raw contacts for a specified account that are not already in a group.
  */
 public class GroupMemberPickerFragment extends
-        ContactEntryListFragment<DefaultContactListAdapter> {
+        MultiSelectContactsListFragment<DefaultContactListAdapter> {
 
     public static final String TAG = "GroupMemberPicker";
 
@@ -55,6 +59,12 @@
 
         /** Invoked when a potential group member is selected. */
         void onGroupMemberClicked(long contactId);
+
+        /** Invoked when multiple potential group members are selected. */
+        void onGroupMembersSelected(long[] contactIds);
+
+        /** Invoked when user has initiated multiple selection mode. */
+        void onSelectGroupMembers();
     }
 
     /** Filters out raw contacts that are already in the group. */
@@ -213,6 +223,10 @@
 
     @Override
     protected void onItemClick(int position, long id) {
+        if (getAdapter().isDisplayingCheckBoxes()) {
+            super.onItemClick(position, id);
+            return;
+        }
         if (mListener != null) {
             final long contactId = getAdapter().getContactId(position);
             if (contactId > 0) {
@@ -220,4 +234,65 @@
             }
         }
     }
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
+        super.onCreateOptionsMenu(menu, menuInflater);
+        menuInflater.inflate(R.menu.group_member_picker, menu);
+    }
+
+    @Override
+    public void onPrepareOptionsMenu(Menu menu) {
+        final ContactSelectionActivity activity = getContactSelectionActivity();
+        final boolean isSearchMode = activity == null ? false : activity.isSearchMode();
+        final boolean isSelectionMode = activity == null ? false : activity.isSelectionMode();
+
+        // Added in ContactSelectionActivity but we must account for selection mode
+        setVisible(menu, R.id.menu_search, !isSearchMode && !isSelectionMode);
+
+        setVisible(menu, R.id.menu_done, !isSearchMode && isSelectionMode &&
+                getAdapter().getSelectedContactIds().size() > 0);
+        setVisible(menu, R.id.menu_select, !isSearchMode && !isSelectionMode);
+    }
+
+    private ContactSelectionActivity getContactSelectionActivity() {
+        final Activity activity = getActivity();
+        if (activity != null && activity instanceof ContactSelectionActivity) {
+            return (ContactSelectionActivity) activity;
+        }
+        return null;
+    }
+
+    private static void setVisible(Menu menu, int id, boolean visible) {
+        final MenuItem menuItem = menu.findItem(id);
+        if (menuItem != null) {
+            menuItem.setVisible(visible);
+        }
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case android.R.id.home: {
+                final Activity activity = getActivity();
+                if (activity != null) {
+                    activity.onBackPressed();
+                }
+                return true;
+            }
+            case R.id.menu_done: {
+                if (mListener != null) {
+                    mListener.onGroupMembersSelected(getAdapter().getSelectedContactIdsArray());
+                }
+                return true;
+            }
+            case R.id.menu_select: {
+                if (mListener != null) {
+                    mListener.onSelectGroupMembers();
+                }
+                return true;
+            }
+        }
+        return super.onOptionsItemSelected(item);
+    }
 }
diff --git a/src/com/android/contacts/list/UiIntentActions.java b/src/com/android/contacts/list/UiIntentActions.java
index 9552942..39d5007 100644
--- a/src/com/android/contacts/list/UiIntentActions.java
+++ b/src/com/android/contacts/list/UiIntentActions.java
@@ -123,4 +123,7 @@
      */
     public static final String TARGET_CONTACT_ID_EXTRA_KEY
             = "com.android.contacts.action.CONTACT_ID";
+
+    public static final String TARGET_CONTACT_IDS_EXTRA_KEY
+            = "com.android.contacts.action.CONTACT_IDS";
 }
\ No newline at end of file