Let GroupMembersFragment control actionbar, menu and more.

* Create mActionBarAdapter from GroupMembersFragment and remove
  mActionBarAdapter from GroupMembersActivity.
* Move creation of group-related menu to GroupMembersFragment.
* Move the following listeners to GroupMembersFragment:
  - ActionBarAdapter.Listener
  - MultiSelectContactsListFragment.OnCheckBoxListActionListener
* Move UpdateGroupMembersAsyncTask a new class.
* Remove mGroupMetadata from GroupMembersActivity and get it from
  GroupMembersFragment instead.
* Remove GroupMembersListener, since the actions can be done within
  GroupMembersFragment now.

Bug 30944495

Change-Id: Ie6a7c51afecdc67f953f185369f64f2192c46bc1
diff --git a/src/com/android/contacts/ContactsDrawerActivity.java b/src/com/android/contacts/ContactsDrawerActivity.java
index bea1411..54bc4ad 100644
--- a/src/com/android/contacts/ContactsDrawerActivity.java
+++ b/src/com/android/contacts/ContactsDrawerActivity.java
@@ -232,6 +232,10 @@
         }
     }
 
+    public Toolbar getToolbar() {
+        return mToolbar;
+    }
+
     private void maybeUpdateScrollPosition(int position) {
         if (mDrawer.isDrawerOpen(GravityCompat.START)) {
             if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "Don't scroll menu when drawer open");
@@ -413,7 +417,8 @@
         }
     }
 
-    protected void updateGroupMenu(GroupMetadata groupMetadata) {
+    // TODO(wenyiw) the method is public for now; we should remove it after b/30944495 is fixed.
+    public void updateGroupMenu(GroupMetadata groupMetadata) {
         clearCheckedMenus();
         if (groupMetadata != null && mGroupMenuMap != null
                 && mGroupMenuMap.get(groupMetadata.groupId) != null) {
diff --git a/src/com/android/contacts/activities/GroupMembersActivity.java b/src/com/android/contacts/activities/GroupMembersActivity.java
index 000cae4..e4b052c 100644
--- a/src/com/android/contacts/activities/GroupMembersActivity.java
+++ b/src/com/android/contacts/activities/GroupMembersActivity.java
@@ -17,163 +17,33 @@
 
 import android.app.FragmentManager;
 import android.app.FragmentTransaction;
-import android.content.Context;
 import android.content.Intent;
-import android.database.Cursor;
 import android.net.Uri;
-import android.os.AsyncTask;
 import android.os.Bundle;
-import android.provider.ContactsContract.RawContacts;
 import android.support.v4.view.GravityCompat;
 import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
 import android.widget.Toast;
 
-import com.android.contacts.ContactSaveService;
 import com.android.contacts.ContactsDrawerActivity;
 import com.android.contacts.R;
-import com.android.contacts.common.GroupMetaData;
-import com.android.contacts.common.logging.ListEvent;
-import com.android.contacts.common.logging.Logger;
-import com.android.contacts.common.logging.ScreenEvent.ScreenType;
-import com.android.contacts.common.model.account.AccountWithDataSet;
-import com.android.contacts.common.util.ImplicitIntentsUtil;
 import com.android.contacts.group.GroupMembersFragment;
 import com.android.contacts.group.GroupMetadata;
-import com.android.contacts.group.GroupNameEditDialogFragment;
 import com.android.contacts.group.GroupUtil;
-import com.android.contacts.interactions.GroupDeletionDialogFragment;
-import com.android.contacts.list.ContactsRequest;
-import com.android.contacts.list.MultiSelectContactsListFragment;
-import com.android.contacts.list.UiIntentActions;
-import com.android.contacts.quickcontact.QuickContactActivity;
 
 /**
  * Displays the members of a group and allows the user to edit it.
  */
-public class GroupMembersActivity extends ContactsDrawerActivity implements
-        ActionBarAdapter.Listener,
-        MultiSelectContactsListFragment.OnCheckBoxListActionListener,
-        GroupMembersFragment.GroupMembersListener {
+public class GroupMembersActivity extends ContactsDrawerActivity {
 
     private static final String TAG = "GroupMembers";
 
     private static final String KEY_GROUP_URI = "groupUri";
-    private static final String KEY_GROUP_METADATA = "groupMetadata";
-    private static final String KEY_IS_EDIT_MODE = "editMode";
 
     private static final String TAG_GROUP_MEMBERS = "groupMembers";
-    private static final String TAG_GROUP_NAME_EDIT_DIALOG = "groupNameEditDialog";
-
-    private static final String ACTION_DELETE_GROUP = "deleteGroup";
-    private static final String ACTION_UPDATE_GROUP = "updateGroup";
-    private static final String ACTION_ADD_TO_GROUP = "addToGroup";
-    private static final String ACTION_REMOVE_FROM_GROUP = "removeFromGroup";
-
-    private static final int RESULT_GROUP_ADD_MEMBER = 100;
-
-    /**
-     * Starts an Intent to add/remove the raw contacts for the given contact IDs to/from a group.
-     * Only the raw contacts that belong to the specified account are added or removed.
-     */
-    private static class UpdateGroupMembersAsyncTask extends AsyncTask<Void, Void, Intent> {
-
-        static final int TYPE_ADD = 0;
-        static final int TYPE_REMOVE = 1;
-
-        private final Context mContext;
-        private final int mType;
-        private final long[] mContactIds;
-        private final long mGroupId;
-        private final String mAccountName;
-        private final String mAccountType;
-
-        private UpdateGroupMembersAsyncTask(int type, Context context, long[] contactIds,
-                long groupId, String accountName, String accountType) {
-            mContext = context;
-            mType = type;
-            mContactIds = contactIds;
-            mGroupId = groupId;
-            mAccountName = accountName;
-            mAccountType = accountType;
-        }
-
-        @Override
-        protected Intent doInBackground(Void... params) {
-            final long[] rawContactIds = getRawContactIds();
-            if (rawContactIds.length == 0) {
-                return null;
-            }
-            final long[] rawContactIdsToAdd;
-            final long[] rawContactIdsToRemove;
-            final String action;
-            if (mType == TYPE_ADD) {
-                rawContactIdsToAdd = rawContactIds;
-                rawContactIdsToRemove = null;
-                action = GroupMembersActivity.ACTION_ADD_TO_GROUP;
-            } else if (mType == TYPE_REMOVE) {
-                rawContactIdsToAdd = null;
-                rawContactIdsToRemove = rawContactIds;
-                action = GroupMembersActivity.ACTION_REMOVE_FROM_GROUP;
-            } else {
-                throw new IllegalStateException("Unrecognized type " + mType);
-            }
-            return ContactSaveService.createGroupUpdateIntent(
-                    mContext, mGroupId, /* newLabel */ null, rawContactIdsToAdd,
-                    rawContactIdsToRemove, GroupMembersActivity.class, action);
-        }
-
-        // TODO(wjang): prune raw contacts that are already in the group; ContactSaveService will
-        // log a warning if the raw contact is already a member and keep going but it is not ideal.
-        private long[] getRawContactIds() {
-            final Uri rawContactUri = RawContacts.CONTENT_URI.buildUpon()
-                    .appendQueryParameter(RawContacts.ACCOUNT_NAME, mAccountName)
-                    .appendQueryParameter(RawContacts.ACCOUNT_TYPE, mAccountType)
-                    .build();
-            final String[] projection = new String[]{RawContacts._ID};
-            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.toString(), selectionArgs, null, null);
-            final long[] rawContactIds = new long[cursor.getCount()];
-            try {
-                int i = 0;
-                while (cursor.moveToNext()) {
-                    rawContactIds[i] = cursor.getLong(0);
-                    i++;
-                }
-            } finally {
-                cursor.close();
-            }
-            return rawContactIds;
-        }
-
-        @Override
-        protected void onPostExecute(Intent intent) {
-            if (intent == null) {
-                Toast.makeText(mContext, R.string.groupSavedErrorToast, Toast.LENGTH_SHORT).show();
-            } else {
-                mContext.startService(intent);
-            }
-        }
-    }
-
-    private ActionBarAdapter mActionBarAdapter;
 
     private GroupMembersFragment mMembersFragment;
 
     private Uri mGroupUri;
-    private boolean mIsEditMode;
-
-    private GroupMetadata mGroupMetadata;
 
     @Override
     public void onCreate(Bundle savedState) {
@@ -182,8 +52,6 @@
         // Parse the Intent
         if (savedState != null) {
             mGroupUri = savedState.getParcelable(KEY_GROUP_URI);
-            mIsEditMode = savedState.getBoolean(KEY_IS_EDIT_MODE);
-            mGroupMetadata = savedState.getParcelable(KEY_GROUP_METADATA);
         } else {
             mGroupUri = getIntent().getData();
             setTitle(getIntent().getStringExtra(GroupUtil.EXTRA_GROUP_NAME));
@@ -196,12 +64,6 @@
         // Set up the view
         setContentView(R.layout.group_members_activity);
 
-        // Set up the action bar
-        mActionBarAdapter = new ActionBarAdapter(this, this, getSupportActionBar(),
-                /* portraitTabs */ null, /* landscapeTabs */ null, mToolbar,
-                R.string.enter_contact_name);
-        mActionBarAdapter.setShowHomeIcon(true);
-
         // Add the members list fragment
         final FragmentManager fragmentManager = getFragmentManager();
         mMembersFragment = (GroupMembersFragment)
@@ -211,26 +73,12 @@
             fragmentManager.beginTransaction().replace(R.id.fragment_container_inner,
                     mMembersFragment, TAG_GROUP_MEMBERS).commitAllowingStateLoss();
         }
-        mMembersFragment.setListener(this);
-        if (mGroupMetadata != null && mGroupMetadata.editable) {
-            mMembersFragment.setCheckBoxListListener(this);
-        }
-
-        // Delay action bar initialization until after the fragment is added
-        final ContactsRequest contactsRequest = new ContactsRequest();
-        contactsRequest.setActionCode(ContactsRequest.ACTION_GROUP);
-        mActionBarAdapter.initialize(savedState, contactsRequest);
     }
 
     @Override
     public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
-        if (mActionBarAdapter != null) {
-            mActionBarAdapter.onSaveInstanceState(outState);
-        }
         outState.putParcelable(KEY_GROUP_URI, mGroupUri);
-        outState.putBoolean(KEY_IS_EDIT_MODE, mIsEditMode);
-        outState.putParcelable(KEY_GROUP_METADATA, mGroupMetadata);
     }
 
     // Invoked with results from the ContactSaveService
@@ -256,14 +104,14 @@
 
             toast(getToastMessageForSaveAction(newIntent.getAction()));
 
-            if (mIsEditMode) {
+            if (mMembersFragment.isEditMode()) {
                 // If we're removing group members one at a time, don't reload the fragment so
                 // the user can continue to remove group members one by one
                 if (getGroupCount() == 1) {
                     // If we're deleting the last group member, exit edit mode
                     onBackPressed();
                 }
-            } else if (!ACTION_REMOVE_FROM_GROUP.equals(newIntent.getAction())) {
+            } else if (!GroupUtil.ACTION_REMOVE_FROM_GROUP.equals(newIntent.getAction())) {
                 replaceGroupMembersFragment();
                 invalidateOptionsMenu();
             }
@@ -271,19 +119,20 @@
     }
 
     private static boolean isDeleteAction(String action) {
-        return ACTION_DELETE_GROUP.equals(action);
+        return GroupUtil.ACTION_DELETE_GROUP.equals(action);
     }
 
     private static boolean isSaveAction(String action) {
-        return ACTION_UPDATE_GROUP.equals(action)
-                || ACTION_ADD_TO_GROUP.equals(action)
-                || ACTION_REMOVE_FROM_GROUP.equals(action);
+        return GroupUtil.ACTION_UPDATE_GROUP.equals(action)
+                || GroupUtil.ACTION_ADD_TO_GROUP.equals(action)
+                || GroupUtil.ACTION_REMOVE_FROM_GROUP.equals(action);
     }
 
     private static int getToastMessageForSaveAction(String action) {
-        if (ACTION_UPDATE_GROUP.equals(action)) return R.string.groupUpdatedToast;
-        if (ACTION_ADD_TO_GROUP.equals(action)) return R.string.groupMembersAddedToast;
-        if (ACTION_REMOVE_FROM_GROUP.equals(action)) return R.string.groupMembersRemovedToast;
+        if (GroupUtil.ACTION_UPDATE_GROUP.equals(action)) return R.string.groupUpdatedToast;
+        if (GroupUtil.ACTION_ADD_TO_GROUP.equals(action)) return R.string.groupMembersAddedToast;
+        if (GroupUtil.ACTION_REMOVE_FROM_GROUP.equals(action))
+            return R.string.groupMembersRemovedToast;
         throw new IllegalArgumentException("Unhanded contact save action " + action);
     }
 
@@ -294,19 +143,15 @@
 
     private void replaceGroupMembersFragment() {
         mMembersFragment = GroupMembersFragment.newInstance(mGroupUri);
-        mMembersFragment.setListener(this);
         final FragmentTransaction transaction = getFragmentManager().beginTransaction();
         addGroupsAndFiltersFragments(transaction);
         transaction.replace(R.id.fragment_container_inner, mMembersFragment, TAG_GROUP_MEMBERS)
                 .commitAllowingStateLoss();
-        if (mGroupMetadata != null && mGroupMetadata.editable) {
-            mMembersFragment.setCheckBoxListListener(this);
-        }
     }
 
     @Override
     protected void onGroupMenuItemClicked(long groupId, String title) {
-        if (mGroupMetadata.groupId != groupId) {
+        if (mMembersFragment.getGroupMetadata().groupId != groupId) {
             super.onGroupMenuItemClicked(groupId, title);
         }
     }
@@ -322,129 +167,6 @@
         finish();
     }
 
-    public boolean isEditMode() {
-        return mIsEditMode;
-    }
-
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        if (mGroupMetadata == null) {
-            // Hide menu options until metadata is fully loaded
-            return false;
-        }
-        super.onCreateOptionsMenu(menu);
-        getMenuInflater().inflate(R.menu.view_group, menu);
-        return true;
-    }
-
-    @Override
-    public boolean onPrepareOptionsMenu(Menu menu) {
-        final boolean isSelectionMode = mActionBarAdapter.isSelectionMode();
-        final boolean isGroupEditable = mGroupMetadata != null && mGroupMetadata.editable;
-        final boolean isGroupReadOnly = mGroupMetadata != null && mGroupMetadata.readOnly;
-
-        setVisible(menu, R.id.menu_add, isGroupEditable && !isSelectionMode);
-        setVisible(menu, R.id.menu_rename_group, !isGroupReadOnly && !isSelectionMode);
-        setVisible(menu, R.id.menu_delete_group, !isGroupReadOnly && !isSelectionMode);
-        setVisible(menu, R.id.menu_edit_group, isGroupEditable && !mIsEditMode && !isSelectionMode
-                && !isGroupEmpty());
-        setVisible(menu, R.id.menu_remove_from_group, isGroupEditable && isSelectionMode &&
-                !mIsEditMode);
-
-        return true;
-    }
-
-    private boolean isGroupEmpty() {
-        return mMembersFragment != null && mMembersFragment.getAdapter() != null &&
-                mMembersFragment.getAdapter().isEmpty();
-    }
-
-    private static void setVisible(Menu menu, int id, boolean visible) {
-        final MenuItem menuItem = menu.findItem(id);
-        if (menuItem != null) {
-            menuItem.setVisible(visible);
-        }
-    }
-
-    public void startGroupAddMemberActivity() {
-        startActivityForResult(GroupUtil.createPickMemberIntent(this, mGroupMetadata,
-                mMembersFragment.getMemberContactIds()), RESULT_GROUP_ADD_MEMBER);
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        switch (item.getItemId()) {
-            case android.R.id.home: {
-                onBackPressed();
-                return true;
-            }
-            case R.id.menu_add: {
-                startGroupAddMemberActivity();
-                return true;
-            }
-            case R.id.menu_rename_group: {
-                GroupNameEditDialogFragment.newInstanceForUpdate(
-                        new AccountWithDataSet(mGroupMetadata.accountName,
-                                mGroupMetadata.accountType, mGroupMetadata.dataSet),
-                        ACTION_UPDATE_GROUP, mGroupMetadata.groupId, mGroupMetadata.groupName)
-                        .show(getFragmentManager(), TAG_GROUP_NAME_EDIT_DIALOG);
-                return true;
-            }
-            case R.id.menu_delete_group: {
-                deleteGroup();
-                return true;
-            }
-            case R.id.menu_edit_group: {
-                if (mMembersFragment == null) {
-                    return false;
-                }
-                mIsEditMode = true;
-                mActionBarAdapter.setSelectionMode(true);
-                mMembersFragment.displayDeleteButtons(true);
-                return true;
-            }
-            case R.id.menu_remove_from_group: {
-                if (mMembersFragment == null) {
-                    return false;
-                }
-                logListEvent();
-                removeSelectedContacts();
-                return true;
-            }
-        }
-        return super.onOptionsItemSelected(item);
-    }
-
-    private void deleteGroup() {
-        if (mMembersFragment.getMemberCount() == 0) {
-            final Intent intent = ContactSaveService.createGroupDeletionIntent(this,
-                    mGroupMetadata.groupId);
-            startService(intent);
-            finish();
-        } else {
-            GroupDeletionDialogFragment.show(getFragmentManager(), mGroupMetadata.groupId,
-                    mGroupMetadata.groupName);
-        }
-    }
-
-    private void logListEvent() {
-        Logger.logListEvent(
-                ListEvent.ActionType.REMOVE_LABEL,
-                mMembersFragment.getListType(),
-                mMembersFragment.getAdapter().getCount(),
-                /* clickedIndex */ -1,
-                mMembersFragment.getAdapter().getSelectedContactIdsArray().length);
-    }
-
-    private void removeSelectedContacts() {
-        final long[] contactIds = mMembersFragment.getAdapter().getSelectedContactIdsArray();
-        new UpdateGroupMembersAsyncTask(UpdateGroupMembersAsyncTask.TYPE_REMOVE,
-                this, contactIds, mGroupMetadata.groupId, mGroupMetadata.accountName,
-                mGroupMetadata.accountType).execute();
-
-        mActionBarAdapter.setSelectionMode(false);
-    }
-
     @Override
     public void onBackPressed() {
         if (!isSafeToCommitTransactions()) {
@@ -452,44 +174,21 @@
         }
         if (mDrawer.isDrawerOpen(GravityCompat.START)) {
             mDrawer.closeDrawer(GravityCompat.START);
-        } else if (mIsEditMode) {
-            mIsEditMode = false;
-            mActionBarAdapter.setSelectionMode(false);
-            if (mMembersFragment != null) {
-                mMembersFragment.displayDeleteButtons(false);
-            }
-        } else if (mActionBarAdapter.isSelectionMode()) {
-            mActionBarAdapter.setSelectionMode(false);
-            if (mMembersFragment != null) {
-                mMembersFragment.displayCheckBoxes(false);
-            }
-        } else if (mActionBarAdapter.isSearchMode()) {
-            mActionBarAdapter.setSearchMode(false);
+        } else if (mMembersFragment.isEditMode()) {
+            mMembersFragment.setEditMode(false);
+            mMembersFragment.getActionBarAdapter().setSelectionMode(false);
+            mMembersFragment.displayDeleteButtons(false);
+        } else if (mMembersFragment.getActionBarAdapter().isSelectionMode()) {
+            mMembersFragment.getActionBarAdapter().setSelectionMode(false);
+            mMembersFragment.displayCheckBoxes(false);
+        } else if (mMembersFragment.getActionBarAdapter().isSearchMode()) {
+            mMembersFragment.getActionBarAdapter().setSearchMode(false);
         } else {
             switchToAllContacts();
             super.onBackPressed();
         }
     }
 
-    @Override
-    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
-        if (requestCode == RESULT_GROUP_ADD_MEMBER && resultCode == RESULT_OK && data != null) {
-            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 UpdateGroupMembersAsyncTask(UpdateGroupMembersAsyncTask.TYPE_ADD,
-                    this, contactIds, mGroupMetadata.groupId, mGroupMetadata.accountName,
-                    mGroupMetadata.accountType).execute();
-        }
-    }
-
     private void setResultCanceledAndFinish(int resId) {
         toast(resId);
         setResult(RESULT_CANCELED);
@@ -502,112 +201,8 @@
         }
     }
 
-    // ActionBarAdapter callbacks
-
-    @Override
-    public void onAction(int action) {
-        switch (action) {
-            case ActionBarAdapter.Listener.Action.START_SELECTION_MODE:
-                if (mMembersFragment != null) {
-                    if (mIsEditMode) {
-                        mMembersFragment.displayDeleteButtons(true);
-                        mActionBarAdapter.setActionBarTitle(getString(R.string.title_edit_group));
-                    } else {
-                        mMembersFragment.displayCheckBoxes(true);
-                    }
-                }
-                invalidateOptionsMenu();
-                showFabWithAnimation(/* showFabWithAnimation = */ false);
-                break;
-            case ActionBarAdapter.Listener.Action.STOP_SEARCH_AND_SELECTION_MODE:
-                mActionBarAdapter.setSearchMode(false);
-                if (mMembersFragment != null) {
-                    if (mIsEditMode) {
-                        mMembersFragment.displayDeleteButtons(false);
-                    } else {
-                        mMembersFragment.displayCheckBoxes(false);
-                    }
-                }
-                invalidateOptionsMenu();
-                showFabWithAnimation(/* showFabWithAnimation */ true);
-                break;
-            case ActionBarAdapter.Listener.Action.BEGIN_STOPPING_SEARCH_AND_SELECTION_MODE:
-                showFabWithAnimation(/* showFabWithAnimation */ true);
-                break;
-        }
-    }
-
-    private void showFabWithAnimation(boolean showFab) {
-        // TODO: b/28497108
-    }
-
-    @Override
-    public void onSelectedTabChanged() {
-    }
-
-    @Override
-    public void onUpButtonPressed() {
-        onBackPressed();
-    }
-
-    // MultiSelect checkbox callbacks
-
-    @Override
-    public void onStartDisplayingCheckBoxes() {
-        mActionBarAdapter.setSelectionMode(true);
-    }
-
-    @Override
-    public void onSelectedContactIdsChanged() {
-        if (mIsEditMode) {
-            mActionBarAdapter.setActionBarTitle(getString(R.string.title_edit_group));
-        } else {
-            mActionBarAdapter.setSelectionCount(mMembersFragment.getSelectedContactIds().size());
-        }
-    }
-
-    @Override
-    public void onStopDisplayingCheckBoxes() {
-        mActionBarAdapter.setSelectionMode(false);
-    }
-
-    // GroupMembersFragment callbacks
-
-    @Override
-    public void onGroupMetadataLoaded(GroupMetadata groupMetadata) {
-        mGroupMetadata = groupMetadata;
-        updateGroupMenu(mGroupMetadata);
-        setTitle(mGroupMetadata.groupName);
-        invalidateOptionsMenu();
-    }
-
-    @Override
-    public void onGroupMetadataLoadFailed() {
-        setResultCanceledAndFinish(R.string.groupLoadErrorToast);
-    }
-
     @Override
     protected GroupMetadata getGroupMetadata() {
-        return mGroupMetadata;
-    }
-
-    @Override
-    public void onGroupMemberListItemClicked(int position, Uri contactLookupUri) {
-        final int count = mMembersFragment.getAdapter().getCount();
-        Logger.logListEvent(ListEvent.ActionType.CLICK, ListEvent.ListType.GROUP, count,
-                /* clickedIndex */ position, /* numSelected */ 0);
-        final Intent intent = ImplicitIntentsUtil.composeQuickContactIntent(this,
-                contactLookupUri, QuickContactActivity.MODE_FULLY_EXPANDED);
-        intent.putExtra(QuickContactActivity.EXTRA_PREVIOUS_SCREEN_TYPE, ScreenType.LIST_GROUP);
-        startActivity(intent);
-    }
-
-    @Override
-    public void onGroupMemberListItemDeleted(int position, long contactId) {
-        final long[] contactIds = new long[1];
-        contactIds[0] = contactId;
-        new UpdateGroupMembersAsyncTask(UpdateGroupMembersAsyncTask.TYPE_REMOVE,
-                this, contactIds, mGroupMetadata.groupId, mGroupMetadata.accountName,
-                mGroupMetadata.accountType).execute();
+        return mMembersFragment.getGroupMetadata();
     }
 }
diff --git a/src/com/android/contacts/common/list/MultiSelectEntryContactListAdapter.java b/src/com/android/contacts/common/list/MultiSelectEntryContactListAdapter.java
index 9ab6e1c..dbfd70e 100644
--- a/src/com/android/contacts/common/list/MultiSelectEntryContactListAdapter.java
+++ b/src/com/android/contacts/common/list/MultiSelectEntryContactListAdapter.java
@@ -113,9 +113,6 @@
      * Not guaranteed to work with all configurations of this adapter.
      */
     public void setDisplayCheckBoxes(boolean showCheckBoxes) {
-        if (!mDisplayCheckBoxes && showCheckBoxes) {
-            setSelectedContactIds(new TreeSet<Long>());
-        }
         mDisplayCheckBoxes = showCheckBoxes;
         notifyDataSetChanged();
         if (mSelectedContactsListener != null) {
diff --git a/src/com/android/contacts/group/GroupMembersFragment.java b/src/com/android/contacts/group/GroupMembersFragment.java
index 769a083..d3c828b 100644
--- a/src/com/android/contacts/group/GroupMembersFragment.java
+++ b/src/com/android/contacts/group/GroupMembersFragment.java
@@ -18,33 +18,49 @@
 import android.app.Activity;
 import android.app.LoaderManager.LoaderCallbacks;
 import android.content.CursorLoader;
+import android.content.Intent;
 import android.content.Loader;
 import android.database.Cursor;
 import android.database.CursorWrapper;
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.ContactsContract.Contacts;
+import android.support.v7.app.AppCompatActivity;
 import android.util.Log;
 import android.view.Gravity;
 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 android.widget.Button;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
+import android.widget.Toast;
 
+import com.android.contacts.ContactSaveService;
 import com.android.contacts.GroupMetaDataLoader;
 import com.android.contacts.R;
+import com.android.contacts.activities.ActionBarAdapter;
 import com.android.contacts.activities.GroupMembersActivity;
 import com.android.contacts.common.list.ContactsSectionIndexer;
 import com.android.contacts.common.list.MultiSelectEntryContactListAdapter;
+import com.android.contacts.common.logging.ListEvent;
 import com.android.contacts.common.logging.ListEvent.ListType;
+import com.android.contacts.common.logging.Logger;
+import com.android.contacts.common.logging.ScreenEvent;
 import com.android.contacts.common.model.AccountTypeManager;
 import com.android.contacts.common.model.account.AccountType;
 import com.android.contacts.common.model.account.AccountWithDataSet;
+import com.android.contacts.common.util.ImplicitIntentsUtil;
 import com.android.contacts.group.GroupMembersAdapter.GroupMembersQuery;
+import com.android.contacts.interactions.GroupDeletionDialogFragment;
+import com.android.contacts.list.ContactsRequest;
 import com.android.contacts.list.MultiSelectContactsListFragment;
+import com.android.contacts.list.UiIntentActions;
+import com.android.contacts.quickcontact.QuickContactActivity;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -53,32 +69,22 @@
 
 /** Displays the members of a group. */
 public class GroupMembersFragment extends MultiSelectContactsListFragment<GroupMembersAdapter>
-        implements MultiSelectEntryContactListAdapter.DeleteContactListener {
+        implements ActionBarAdapter.Listener,
+        MultiSelectContactsListFragment.OnCheckBoxListActionListener,
+        MultiSelectEntryContactListAdapter.DeleteContactListener {
 
     private static final String TAG = "GroupMembers";
 
+    private static final String KEY_IS_EDIT_MODE = "editMode";
     private static final String KEY_GROUP_URI = "groupUri";
     private static final String KEY_GROUP_METADATA = "groupMetadata";
 
+    public static final String TAG_GROUP_NAME_EDIT_DIALOG = "groupNameEditDialog";
+
     private static final String ARG_GROUP_URI = "groupUri";
 
     private static final int LOADER_GROUP_METADATA = 0;
-
-    /** Callbacks for hosts of {@link GroupMembersFragment}. */
-    public interface GroupMembersListener {
-
-        /** Invoked after group metadata for the passed in group URI has loaded. */
-        void onGroupMetadataLoaded(GroupMetadata groupMetadata);
-
-        /** Invoked if group metadata can't be loaded for the passed in group URI. */
-        void onGroupMetadataLoadFailed();
-
-        /** Invoked when a group member in the list is clicked. */
-        void onGroupMemberListItemClicked(int position, Uri contactLookupUri);
-
-        /** Invoked when a the delete button for a group member in the list is clicked. */
-        void onGroupMemberListItemDeleted(int position, long contactId);
-    }
+    private static final int RESULT_GROUP_ADD_MEMBER = 100;
 
     /** Filters out duplicate contacts. */
     private class FilterCursorWrapper extends CursorWrapper {
@@ -191,9 +197,11 @@
         public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
             if (cursor == null || cursor.isClosed() || !cursor.moveToNext()) {
                 Log.e(TAG, "Failed to load group metadata for " + mGroupUri);
-                if (mListener != null) {
-                    mListener.onGroupMetadataLoadFailed();
-                }
+                final Activity activity = getActivity();
+                Toast.makeText(getContext(), R.string.groupLoadErrorToast, Toast.LENGTH_SHORT)
+                        .show();
+                activity.setResult(AppCompatActivity.RESULT_CANCELED);
+                activity.finish();
                 return;
             }
             mGroupMetadata = new GroupMetadata();
@@ -218,9 +226,11 @@
         public void onLoaderReset(Loader<Cursor> loader) {}
     };
 
+    private ActionBarAdapter mActionBarAdapter;
+
     private Uri mGroupUri;
 
-    private GroupMembersListener mListener;
+    private boolean mIsEditMode;
 
     private GroupMetadata mGroupMetadata;
 
@@ -243,8 +253,215 @@
         setListType(ListType.GROUP);
     }
 
-    public void setListener(GroupMembersListener listener) {
-        mListener = listener;
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        if (mGroupMetadata == null) {
+            // Hide menu options until metadata is fully loaded
+            return;
+        }
+        super.onCreateOptionsMenu(menu, inflater);
+        inflater.inflate(R.menu.view_group, menu);
+    }
+
+    @Override
+    public void onPrepareOptionsMenu(Menu menu) {
+        final boolean isSelectionMode = mActionBarAdapter.isSelectionMode();
+        final boolean isGroupEditable = mGroupMetadata != null && mGroupMetadata.editable;
+        final boolean isGroupReadOnly = mGroupMetadata != null && mGroupMetadata.readOnly;
+
+        setVisible(menu, R.id.menu_add, isGroupEditable && !isSelectionMode);
+        setVisible(menu, R.id.menu_rename_group, !isGroupReadOnly && !isSelectionMode);
+        setVisible(menu, R.id.menu_delete_group, !isGroupReadOnly && !isSelectionMode);
+        setVisible(menu, R.id.menu_edit_group, isGroupEditable && !mIsEditMode && !isSelectionMode
+                && !isGroupEmpty());
+        setVisible(menu, R.id.menu_remove_from_group, isGroupEditable && isSelectionMode &&
+                !mIsEditMode);
+    }
+
+    private boolean isGroupEmpty() {
+        return getAdapter() != null && getAdapter().isEmpty();
+    }
+
+    private static void setVisible(Menu menu, int id, boolean visible) {
+        final MenuItem menuItem = menu.findItem(id);
+        if (menuItem != null) {
+            menuItem.setVisible(visible);
+        }
+    }
+
+    private void startGroupAddMemberActivity() {
+        startActivityForResult(GroupUtil.createPickMemberIntent(getContext(), mGroupMetadata,
+                getMemberContactIds()), RESULT_GROUP_ADD_MEMBER);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case android.R.id.home: {
+                getActivity().onBackPressed();
+                return true;
+            }
+            case R.id.menu_add: {
+                startGroupAddMemberActivity();
+                return true;
+            }
+            case R.id.menu_rename_group: {
+                GroupNameEditDialogFragment.newInstanceForUpdate(
+                        new AccountWithDataSet(mGroupMetadata.accountName,
+                                mGroupMetadata.accountType, mGroupMetadata.dataSet),
+                        GroupUtil.ACTION_UPDATE_GROUP, mGroupMetadata.groupId,
+                        mGroupMetadata.groupName).show(getFragmentManager(),
+                        TAG_GROUP_NAME_EDIT_DIALOG);
+                return true;
+            }
+            case R.id.menu_delete_group: {
+                deleteGroup();
+                return true;
+            }
+            case R.id.menu_edit_group: {
+                mIsEditMode = true;
+                mActionBarAdapter.setSelectionMode(true);
+                displayDeleteButtons(true);
+                return true;
+            }
+            case R.id.menu_remove_from_group: {
+                logListEvent();
+                removeSelectedContacts();
+                return true;
+            }
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    private void removeSelectedContacts() {
+        final long[] contactIds = getAdapter().getSelectedContactIdsArray();
+        new UpdateGroupMembersAsyncTask(UpdateGroupMembersAsyncTask.TYPE_REMOVE,
+                getContext(), contactIds, mGroupMetadata.groupId, mGroupMetadata.accountName,
+                mGroupMetadata.accountType).execute();
+
+        mActionBarAdapter.setSelectionMode(false);
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode == RESULT_GROUP_ADD_MEMBER && resultCode ==
+                Activity.RESULT_OK && data != null) {
+            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 UpdateGroupMembersAsyncTask(
+                    UpdateGroupMembersAsyncTask.TYPE_ADD,
+                    getContext(), contactIds, mGroupMetadata.groupId, mGroupMetadata.accountName,
+                    mGroupMetadata.accountType).execute();
+        }
+    }
+
+    // ActionBarAdapter callbacks
+
+    @Override
+    public void onAction(int action) {
+        switch (action) {
+            case ActionBarAdapter.Listener.Action.START_SELECTION_MODE:
+                if (mIsEditMode) {
+                    displayDeleteButtons(true);
+                    getActionBarAdapter().setActionBarTitle(getString(R.string.title_edit_group));
+                } else {
+                    displayCheckBoxes(true);
+                }
+                getActivity().invalidateOptionsMenu();
+                break;
+            case ActionBarAdapter.Listener.Action.STOP_SEARCH_AND_SELECTION_MODE:
+                getActionBarAdapter().setSearchMode(false);
+                if (mIsEditMode) {
+                    displayDeleteButtons(false);
+                } else {
+                    displayCheckBoxes(false);
+                }
+                getActivity().invalidateOptionsMenu();
+                break;
+            case ActionBarAdapter.Listener.Action.BEGIN_STOPPING_SEARCH_AND_SELECTION_MODE:
+                break;
+        }
+    }
+
+    @Override
+    public void onSelectedTabChanged() {
+    }
+
+    @Override
+    public void onUpButtonPressed() {
+        getActivity().onBackPressed();
+    }
+
+    // MultiSelect checkbox callbacks
+
+    @Override
+    public void onStartDisplayingCheckBoxes() {
+        mActionBarAdapter.setSelectionMode(true);
+    }
+
+    @Override
+    public void onSelectedContactIdsChanged() {
+        if (mActionBarAdapter == null) {
+            return;
+        }
+        if (mIsEditMode) {
+            mActionBarAdapter.setActionBarTitle(getString(R.string.title_edit_group));
+        } else {
+            mActionBarAdapter.setSelectionCount(getSelectedContactIds().size());
+        }
+    }
+
+    @Override
+    public void onStopDisplayingCheckBoxes() {
+        mActionBarAdapter.setSelectionMode(false);
+    }
+
+    private void logListEvent() {
+        Logger.logListEvent(
+                ListEvent.ActionType.REMOVE_LABEL,
+                getListType(),
+                getAdapter().getCount(),
+                /* clickedIndex */ -1,
+                getAdapter().getSelectedContactIdsArray().length);
+    }
+
+    private void deleteGroup() {
+        if (getMemberCount() == 0) {
+            final Intent intent = ContactSaveService.createGroupDeletionIntent(
+                    getContext(), mGroupMetadata.groupId);
+            getContext().startService(intent);
+            getActivity().finish();
+        } else {
+            GroupDeletionDialogFragment.show(getFragmentManager(), mGroupMetadata.groupId,
+                    mGroupMetadata.groupName);
+        }
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        // This is the first safe place in the fragment lifecycle to know getActivity() will not
+        // be null (i.e. it can be null in onCreateView() of this fragment)
+        final GroupMembersActivity activity = (GroupMembersActivity) getActivity();
+        mActionBarAdapter = new ActionBarAdapter(activity, this, activity.getSupportActionBar(),
+                /* portraitTabs */ null, /* landscapeTabs */ null, activity.getToolbar(),
+                R.string.enter_contact_name);
+        mActionBarAdapter.setShowHomeIcon(true);
+        final ContactsRequest contactsRequest = new ContactsRequest();
+        contactsRequest.setActionCode(ContactsRequest.ACTION_GROUP);
+        mActionBarAdapter.initialize(savedInstanceState, contactsRequest);
+    }
+
+    public ActionBarAdapter getActionBarAdapter() {
+        return mActionBarAdapter;
     }
 
     public void displayDeleteButtons(boolean displayDeleteButtons) {
@@ -259,15 +476,27 @@
         return mGroupMemberContactIds.size();
     }
 
+    public boolean isEditMode() {
+        return mIsEditMode;
+    }
+
+    public void setEditMode(boolean isEditMode) {
+        mIsEditMode = isEditMode;
+    }
+
     @Override
     public void onCreate(Bundle savedState) {
         super.onCreate(savedState);
         if (savedState == null) {
             mGroupUri = getArguments().getParcelable(ARG_GROUP_URI);
         } else {
+            mIsEditMode = savedState.getBoolean(KEY_IS_EDIT_MODE);
             mGroupUri = savedState.getParcelable(KEY_GROUP_URI);
             mGroupMetadata = savedState.getParcelable(KEY_GROUP_METADATA);
         }
+        if (mGroupMetadata != null && mGroupMetadata.editable) {
+            setCheckBoxListListener(this);
+        }
     }
 
     @Override
@@ -312,6 +541,10 @@
     @Override
     public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
+        if (mActionBarAdapter != null) {
+            mActionBarAdapter.onSaveInstanceState(outState);
+        }
+        outState.putBoolean(KEY_IS_EDIT_MODE, mIsEditMode);
         outState.putParcelable(KEY_GROUP_URI, mGroupUri);
         outState.putParcelable(KEY_GROUP_METADATA, mGroupMetadata);
     }
@@ -321,9 +554,10 @@
 
         maybeAttachCheckBoxListener();
 
-        if (mListener != null) {
-            mListener.onGroupMetadataLoaded(mGroupMetadata);
-        }
+        final GroupMembersActivity activity = (GroupMembersActivity) getActivity();
+        activity.updateGroupMenu(mGroupMetadata);
+        activity.setTitle(mGroupMetadata.groupName);
+        activity.invalidateOptionsMenu();
 
         // Start loading the group members
         super.startLoading();
@@ -333,7 +567,7 @@
         // Don't attach the multi select check box listener if we can't edit the group
         if (mGroupMetadata != null && mGroupMetadata.editable) {
             try {
-                setCheckBoxListListener((OnCheckBoxListActionListener) getActivity());
+                setCheckBoxListListener(this);
             } catch (ClassCastException e) {
                 throw new ClassCastException(getActivity() + " must implement " +
                         OnCheckBoxListActionListener.class.getSimpleName());
@@ -380,7 +614,8 @@
         addContactsButton.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                ((GroupMembersActivity) getActivity()).startGroupAddMemberActivity();
+                startActivityForResult(GroupUtil.createPickMemberIntent(getContext(),
+                        mGroupMetadata, getMemberContactIds()), RESULT_GROUP_ADD_MEMBER);
             }
         });
         return view;
@@ -396,16 +631,21 @@
             super.onItemClick(position, id);
             return;
         }
-        if (mListener != null) {
-            mListener.onGroupMemberListItemClicked(position, uri);
-        }
+        final int count = getAdapter().getCount();
+        Logger.logListEvent(ListEvent.ActionType.CLICK, ListEvent.ListType.GROUP, count,
+                /* clickedIndex */ position, /* numSelected */ 0);
+        final Intent intent = ImplicitIntentsUtil.composeQuickContactIntent(
+                getContext(), uri, QuickContactActivity.MODE_FULLY_EXPANDED);
+        intent.putExtra(
+                QuickContactActivity.EXTRA_PREVIOUS_SCREEN_TYPE, ScreenEvent.ScreenType.LIST_GROUP);
+        startActivity(intent);
     }
 
     @Override
     protected boolean onItemLongClick(int position, long id) {
         final Activity activity = getActivity();
         if (activity != null && activity instanceof GroupMembersActivity) {
-            if (((GroupMembersActivity) activity).isEditMode()) {
+            if (mIsEditMode) {
                 return true;
             }
         }
@@ -415,6 +655,14 @@
     @Override
     public void onContactDeleteClicked(int position) {
         final long contactId = getAdapter().getContactId(position);
-        mListener.onGroupMemberListItemDeleted(position, contactId);
+        final long[] contactIds = new long[1];
+        contactIds[0] = contactId;
+        new UpdateGroupMembersAsyncTask(UpdateGroupMembersAsyncTask.TYPE_REMOVE,
+                getContext(), contactIds, mGroupMetadata.groupId, mGroupMetadata.accountName,
+                mGroupMetadata.accountType).execute();
+    }
+
+    public GroupMetadata getGroupMetadata() {
+        return mGroupMetadata;
     }
 }
diff --git a/src/com/android/contacts/group/GroupUtil.java b/src/com/android/contacts/group/GroupUtil.java
index b9a6425..32517fc 100644
--- a/src/com/android/contacts/group/GroupUtil.java
+++ b/src/com/android/contacts/group/GroupUtil.java
@@ -28,6 +28,7 @@
 import android.widget.ImageView;
 
 import com.android.contacts.GroupListLoader;
+import com.android.contacts.activities.ContactSelectionActivity;
 import com.android.contacts.activities.GroupMembersActivity;
 import com.android.contacts.common.ContactPhotoManager;
 import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
@@ -49,6 +50,11 @@
 @NeededForTesting
 public final class GroupUtil {
 
+    public static final String ACTION_DELETE_GROUP = "deleteGroup";
+    public static final String ACTION_UPDATE_GROUP = "updateGroup";
+    public static final String ACTION_ADD_TO_GROUP = "addToGroup";
+    public static final String ACTION_REMOVE_FROM_GROUP = "removeFromGroup";
+
     // System IDs of FFC groups in Google accounts
     private static final Set<String> FFC_GROUPS =
             new HashSet(Arrays.asList("Friends", "Family", "Coworkers"));
@@ -130,7 +136,7 @@
     /** Returns an Intent to pick contacts to add to a group. */
     public static Intent createPickMemberIntent(Context context,
             GroupMetadata groupMetadata, ArrayList<String> memberContactIds) {
-        final Intent intent = new Intent(context, GroupMembersActivity.class);
+        final Intent intent = new Intent(context, ContactSelectionActivity.class);
         intent.setAction(Intent.ACTION_PICK);
         intent.setType(Groups.CONTENT_TYPE);
         intent.putExtra(UiIntentActions.GROUP_ACCOUNT_NAME, groupMetadata.accountName);
diff --git a/src/com/android/contacts/group/UpdateGroupMembersAsyncTask.java b/src/com/android/contacts/group/UpdateGroupMembersAsyncTask.java
new file mode 100644
index 0000000..a040ddc
--- /dev/null
+++ b/src/com/android/contacts/group/UpdateGroupMembersAsyncTask.java
@@ -0,0 +1,121 @@
+/*
+ * 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
+ */
+
+package com.android.contacts.group;
+
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.provider.ContactsContract;
+import android.widget.Toast;
+
+import com.android.contacts.ContactSaveService;
+import com.android.contacts.R;
+import com.android.contacts.activities.GroupMembersActivity;
+
+/**
+ * Starts an Intent to add/remove the raw contacts for the given contact IDs to/from a group.
+ * Only the raw contacts that belong to the specified account are added or removed.
+ */
+public class UpdateGroupMembersAsyncTask extends AsyncTask<Void, Void, Intent> {
+    static final int TYPE_ADD = 0;
+    static final int TYPE_REMOVE = 1;
+
+    private final Context mContext;
+    private final int mType;
+    private final long[] mContactIds;
+    private final long mGroupId;
+    private final String mAccountName;
+    private final String mAccountType;
+
+    public UpdateGroupMembersAsyncTask(int type, Context context, long[] contactIds,
+            long groupId, String accountName, String accountType) {
+        mContext = context;
+        mType = type;
+        mContactIds = contactIds;
+        mGroupId = groupId;
+        mAccountName = accountName;
+        mAccountType = accountType;
+    }
+
+    @Override
+    protected Intent doInBackground(Void... params) {
+        final long[] rawContactIds = getRawContactIds();
+        if (rawContactIds.length == 0) {
+            return null;
+        }
+        final long[] rawContactIdsToAdd;
+        final long[] rawContactIdsToRemove;
+        final String action;
+        if (mType == TYPE_ADD) {
+            rawContactIdsToAdd = rawContactIds;
+            rawContactIdsToRemove = null;
+            action = GroupUtil.ACTION_ADD_TO_GROUP;
+        } else if (mType == TYPE_REMOVE) {
+            rawContactIdsToAdd = null;
+            rawContactIdsToRemove = rawContactIds;
+            action = GroupUtil.ACTION_REMOVE_FROM_GROUP;
+        } else {
+            throw new IllegalStateException("Unrecognized type " + mType);
+        }
+        return ContactSaveService.createGroupUpdateIntent(
+                mContext, mGroupId, /* newLabel */ null, rawContactIdsToAdd,
+                rawContactIdsToRemove, GroupMembersActivity.class, action);
+    }
+
+    // TODO(wjang): prune raw contacts that are already in the group; ContactSaveService will
+    // log a warning if the raw contact is already a member and keep going but it is not ideal.
+    private long[] getRawContactIds() {
+        final Uri rawContactUri = ContactsContract.RawContacts.CONTENT_URI.buildUpon()
+                .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, mAccountName)
+                .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, mAccountType)
+                .build();
+        final String[] projection = new String[]{ContactsContract.RawContacts._ID};
+        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(ContactsContract.RawContacts.CONTACT_ID).append("=?");
+            selectionArgs[i] = Long.toString(mContactIds[i]);
+        }
+        final Cursor cursor = mContext.getContentResolver().query(
+                rawContactUri, projection, selection.toString(), selectionArgs, null, null);
+        final long[] rawContactIds = new long[cursor.getCount()];
+        try {
+            int i = 0;
+            while (cursor.moveToNext()) {
+                rawContactIds[i] = cursor.getLong(0);
+                i++;
+            }
+        } finally {
+            cursor.close();
+        }
+        return rawContactIds;
+    }
+
+    @Override
+    protected void onPostExecute(Intent intent) {
+        if (intent == null) {
+            Toast.makeText(mContext, R.string.groupSavedErrorToast, Toast.LENGTH_SHORT).show();
+        } else {
+            mContext.startService(intent);
+        }
+    }
+}
diff --git a/src/com/android/contacts/list/MultiSelectContactsListFragment.java b/src/com/android/contacts/list/MultiSelectContactsListFragment.java
index f5c6d34..ea74834 100644
--- a/src/com/android/contacts/list/MultiSelectContactsListFragment.java
+++ b/src/com/android/contacts/list/MultiSelectContactsListFragment.java
@@ -129,13 +129,18 @@
             final TreeSet<Long> selectedContactIds = (TreeSet<Long>)
                     savedInstanceState.getSerializable(EXTRA_KEY_SELECTED_CONTACTS);
             getAdapter().setSelectedContactIds(selectedContactIds);
-            if (mCheckBoxListListener != null) {
-                mCheckBoxListListener.onSelectedContactIdsChanged();
-            }
             mSearchResultClicked = savedInstanceState.getBoolean(KEY_SEARCH_RESULT_CLICKED);
         }
     }
 
+    @Override
+    public void onStart() {
+        super.onStart();
+        if (mCheckBoxListListener != null) {
+            mCheckBoxListListener.onSelectedContactIdsChanged();
+        }
+    }
+
     public TreeSet<Long> getSelectedContactIds() {
         return getAdapter().getSelectedContactIds();
     }