Multi select remove from group members list (1/2)

Bug 18641067

Change-Id: I8968ce992615e2b64ada7caa8b9ce7ea08ee0048
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index c2c4eab..16bd38a 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -245,7 +245,7 @@
 
         <!-- Displays the members of a group in a list -->
         <activity android:name=".activities.GroupMembersActivity"
-            android:theme="@style/ContactPickerTheme"/>
+            android:theme="@style/PeopleActivityTheme"/>
 
         <!-- Views the details of a single group -->
         <activity android:name=".activities.GroupDetailActivity"
diff --git a/res/layout/group_members_activity.xml b/res/layout/group_members_activity.xml
index e8cc594..387fdd2 100644
--- a/res/layout/group_members_activity.xml
+++ b/res/layout/group_members_activity.xml
@@ -19,4 +19,10 @@
     android:id="@+id/fragment_container"
     android:orientation="vertical"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"/>
\ No newline at end of file
+    android:layout_height="match_parent">
+
+    <include
+        layout="@layout/people_activity_toolbar"
+        android:id="@+id/toolbar_parent" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/menu/view_group.xml b/res/menu/view_group.xml
index 669f401..6c5979e 100644
--- a/res/menu/view_group.xml
+++ b/res/menu/view_group.xml
@@ -14,13 +14,20 @@
      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_edit_group"
+        android:icon="@drawable/ic_create_24dp"
         android:title="@string/menu_editGroup"
-        android:alphabeticShortcut="e" />
+        contacts:showAsAction="ifRoom" />
 
     <item
         android:id="@+id/menu_delete_group"
         android:title="@string/menu_deleteGroup" />
+
+    <item
+        android:id="@+id/menu_remove_from_group"
+        android:title="@string/menu_removeFromGroup" />
 </menu>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 37e276f..e3c5146 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -135,7 +135,10 @@
     <string name="menu_editGroup">Edit</string>
 
     <!-- Menu item that deletes the currently selected label [CHAR LIMIT=30] -->
-    <string name="menu_deleteGroup">Delete</string>
+    <string name="menu_deleteGroup">Delete label</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>
 
     <!-- Menu item (in the action bar) that creates a new contact [CHAR LIMIT=30] -->
     <string name="menu_new_contact_action_bar">Add Contact</string>
diff --git a/src/com/android/contacts/activities/GroupMembersActivity.java b/src/com/android/contacts/activities/GroupMembersActivity.java
index e13bcd6..5a1f8e2 100644
--- a/src/com/android/contacts/activities/GroupMembersActivity.java
+++ b/src/com/android/contacts/activities/GroupMembersActivity.java
@@ -15,25 +15,34 @@
  */
 package com.android.contacts.activities;
 
-import android.app.ActionBar;
 import android.app.FragmentManager;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
+import android.support.v7.widget.Toolbar;
+import android.widget.Toast;
 
-import com.android.contacts.ContactsActivity;
+import com.android.contacts.AppCompatContactsActivity;
 import com.android.contacts.R;
 import com.android.contacts.common.util.ImplicitIntentsUtil;
 import com.android.contacts.group.GroupMembersListFragment;
 import com.android.contacts.group.GroupMembersListFragment.GroupMembersListCallbacks;
+import com.android.contacts.list.ContactsRequest;
+import com.android.contacts.list.MultiSelectContactsListFragment;
 import com.android.contacts.quickcontact.QuickContactActivity;
 
 /** Displays the members of a group. */
-public class GroupMembersActivity extends ContactsActivity implements GroupMembersListCallbacks {
+public class GroupMembersActivity extends AppCompatContactsActivity implements
+        ActionBarAdapter.Listener,
+        MultiSelectContactsListFragment.OnCheckBoxListActionListener,
+        GroupMembersListCallbacks {
 
     private static final String TAG_GROUP_MEMBERS = "group_members";
 
+    public static final String ACTION_SAVE_COMPLETED = "saveCompleted";
+
     private GroupMembersListFragment mFragment;
+    private ActionBarAdapter mActionBarAdapter;
 
     @Override
     public void onCreate(Bundle savedState) {
@@ -41,13 +50,7 @@
 
         setContentView(R.layout.group_members_activity);
 
-        final ActionBar actionBar = getActionBar();
-        if (actionBar != null) {
-            actionBar.setDisplayShowHomeEnabled(true);
-            actionBar.setDisplayHomeAsUpEnabled(true);
-            actionBar.setDisplayShowTitleEnabled(true);
-        }
-
+        // Add the group members list fragment
         final FragmentManager fragmentManager = getFragmentManager();
         mFragment = (GroupMembersListFragment) fragmentManager.findFragmentByTag(TAG_GROUP_MEMBERS);
         if (mFragment == null) {
@@ -58,6 +61,55 @@
         }
         mFragment.setGroupUri(getIntent().getData());
         mFragment.setCallbacks(this);
+        mFragment.setCheckBoxListListener(this);
+
+        // Set up the action bar
+        final Toolbar toolbar = getView(R.id.toolbar);
+        setSupportActionBar(toolbar);
+        final ContactsRequest contactsRequest = new ContactsRequest();
+        contactsRequest.setActionCode(ContactsRequest.ACTION_GROUP);
+        mActionBarAdapter = new ActionBarAdapter(this, this, getSupportActionBar(),
+                /* portraitTabs */ null, /* landscapeTabs */ null, toolbar);
+        mActionBarAdapter.setShowHomeIcon(true);
+        mActionBarAdapter.setShowHomeAsUp(true);
+        mActionBarAdapter.initialize(savedState, contactsRequest);
+    }
+
+    @Override
+    protected void onNewIntent(Intent newIntent) {
+        super.onNewIntent(newIntent);
+        if (mFragment != null && ACTION_SAVE_COMPLETED.equals(newIntent.getAction())) {
+            final Uri groupUri = newIntent.getData();
+            Toast.makeText(this,
+                    groupUri == null ? R.string.groupSavedErrorToast :R.string.groupSavedToast,
+                    Toast.LENGTH_SHORT).show();
+
+            if (groupUri != null) {
+                final Intent intent = new Intent(this, GroupMembersActivity.class);
+                intent.setData(groupUri);
+                intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+                startActivity(intent);
+            }
+
+            finish();
+        }
+    }
+
+    /** Whether the ActionBar is currently in selection mode. */
+    public boolean isSelectionMode() {
+        return mActionBarAdapter.isSelectionMode();
+    }
+
+    @Override
+    public void onBackPressed() {
+        if (mActionBarAdapter.isSelectionMode()) {
+            mActionBarAdapter.setSelectionMode(false);
+            mFragment.displayCheckBoxes(false);
+        } else if (mActionBarAdapter.isSearchMode()) {
+            mActionBarAdapter.setSearchMode(false);
+        } else {
+            super.onBackPressed();
+        }
     }
 
     @Override
@@ -67,7 +119,7 @@
 
     @Override
     public void onGroupNameLoaded(String groupName) {
-        setTitle(groupName);
+        getSupportActionBar().setTitle(groupName);
     }
 
     @Override
@@ -83,4 +135,62 @@
         intent.setAction(Intent.ACTION_EDIT);
         startActivity(intent);
     }
+
+    @Override
+    public void onAction(int action) {
+        switch (action) {
+            case ActionBarAdapter.Listener.Action.CHANGE_SEARCH_QUERY:
+                // TODO(wjang)
+                break;
+            case ActionBarAdapter.Listener.Action.START_SEARCH_MODE:
+                mActionBarAdapter.setSearchMode(true);
+                invalidateOptionsMenu();
+                showFabWithAnimation(/* showFabWithAnimation = */ false);
+                break;
+            case ActionBarAdapter.Listener.Action.START_SELECTION_MODE:
+                mFragment.displayCheckBoxes(true);
+                invalidateOptionsMenu();
+                break;
+            case ActionBarAdapter.Listener.Action.STOP_SEARCH_AND_SELECTION_MODE:
+                mActionBarAdapter.setSearchMode(false);
+                mFragment.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(wjang): b/28497108
+    }
+
+    @Override
+    public void onSelectedTabChanged() {
+    }
+
+    @Override
+    public void onUpButtonPressed() {
+        onBackPressed();
+    }
+
+    @Override
+    public void onStartDisplayingCheckBoxes() {
+        mActionBarAdapter.setSelectionMode(true);
+        invalidateOptionsMenu();
+    }
+
+    @Override
+    public void onSelectedContactIdsChanged() {
+        mActionBarAdapter.setSelectionCount(mFragment.getSelectedContactIds().size());
+        invalidateOptionsMenu();
+    }
+
+    @Override
+    public void onStopDisplayingCheckBoxes() {
+        mActionBarAdapter.setSelectionMode(false);
+        invalidateOptionsMenu();
+    }
 }
diff --git a/src/com/android/contacts/group/GroupMembersListAdapter.java b/src/com/android/contacts/group/GroupMembersListAdapter.java
index f208fc8..167c014 100644
--- a/src/com/android/contacts/group/GroupMembersListAdapter.java
+++ b/src/com/android/contacts/group/GroupMembersListAdapter.java
@@ -39,6 +39,7 @@
 
         private static final String[] PROJECTION_PRIMARY = new String[] {
                 Data.CONTACT_ID,
+                Data.RAW_CONTACT_ID,
                 Data.PHOTO_ID,
                 Data.LOOKUP_KEY,
                 Data.CONTACT_PRESENCE,
@@ -48,6 +49,7 @@
 
         private static final String[] PROJECTION_ALTERNATIVE = new String[] {
                 Data.CONTACT_ID,
+                Data.RAW_CONTACT_ID,
                 Data.PHOTO_ID,
                 Data.LOOKUP_KEY,
                 Data.CONTACT_PRESENCE,
@@ -56,18 +58,19 @@
         };
 
         public static final int CONTACT_ID                   = 0;
-        public static final int CONTACT_PHOTO_ID             = 1;
-        public static final int CONTACT_LOOKUP_KEY           = 2;
-        public static final int CONTACT_PRESENCE             = 3;
-        public static final int CONTACT_STATUS               = 4;
-        public static final int CONTACT_DISPLAY_NAME         = 5;
+        public static final int RAW_CONTACT_ID               = 1;
+        public static final int CONTACT_PHOTO_ID             = 2;
+        public static final int CONTACT_LOOKUP_KEY           = 3;
+        public static final int CONTACT_PRESENCE             = 4;
+        public static final int CONTACT_STATUS               = 5;
+        public static final int CONTACT_DISPLAY_NAME         = 6;
     }
 
     private final CharSequence mUnknownNameText;
     private long mGroupId;
 
     public GroupMembersListAdapter(Context context) {
-        super(context, GroupMembersQuery.CONTACT_ID);
+        super(context, GroupMembersQuery.RAW_CONTACT_ID);
         mUnknownNameText = context.getText(android.R.string.unknownName);
         setIndexedPartition(0);
     }
@@ -116,6 +119,13 @@
         return ((Cursor) getItem(position)).getString(GroupMembersQuery.CONTACT_DISPLAY_NAME);
     }
 
+    public Uri getContactUri(int position) {
+        final Cursor cursor = (Cursor) getItem(position);
+        final long contactId = cursor.getLong(GroupMembersQuery.CONTACT_ID);
+        final String lookupKey = cursor.getString(GroupMembersQuery.CONTACT_LOOKUP_KEY);
+        return Contacts.getLookupUri(contactId, lookupKey);
+    }
+
     @Override
     protected ContactListItemView newView(Context context, int partition, Cursor cursor,
             int position, ViewGroup parent) {
diff --git a/src/com/android/contacts/group/GroupMembersListFragment.java b/src/com/android/contacts/group/GroupMembersListFragment.java
index 6fe206d..fa3de1b 100644
--- a/src/com/android/contacts/group/GroupMembersListFragment.java
+++ b/src/com/android/contacts/group/GroupMembersListFragment.java
@@ -18,6 +18,7 @@
 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.net.Uri;
@@ -34,10 +35,11 @@
 import android.view.ViewGroup;
 import android.widget.TextView;
 
+import com.android.contacts.ContactSaveService;
 import com.android.contacts.GroupListLoader;
 import com.android.contacts.GroupMetaDataLoader;
 import com.android.contacts.R;
-import com.android.contacts.common.list.ContactEntryListFragment;
+import com.android.contacts.activities.GroupMembersActivity;
 import com.android.contacts.common.model.AccountTypeManager;
 import com.android.contacts.common.model.account.AccountType;
 import com.android.contacts.interactions.GroupDeletionDialogFragment;
@@ -270,10 +272,18 @@
     @Override
     public void onPrepareOptionsMenu(Menu menu) {
         final MenuItem editMenu = menu.findItem(R.id.menu_edit_group);
-        editMenu.setVisible(isGroupEditable());
+        editMenu.setVisible(!isSelectionMode() && isGroupEditable());
 
         final MenuItem deleteMenu = menu.findItem(R.id.menu_delete_group);
-        deleteMenu.setVisible(isGroupDeletable());
+        deleteMenu.setVisible(!isSelectionMode() && isGroupDeletable());
+
+        final MenuItem removeFromGroupMenu = menu.findItem(R.id.menu_remove_from_group);
+        removeFromGroupMenu.setVisible(isSelectionMode());
+    }
+
+    private boolean isSelectionMode() {
+        final GroupMembersActivity activity = (GroupMembersActivity) getActivity();
+        return activity != null && activity.isSelectionMode();
     }
 
     private boolean isGroupEditable() {
@@ -304,10 +314,26 @@
                         mGroupMetadata.groupName, /* endActivity */ true);
                 return true;
             }
+            case R.id.menu_remove_from_group: {
+                removeSelectedContacts();
+                return true;
+            }
         }
         return false;
     }
 
+    private void removeSelectedContacts() {
+        final Activity activity = getActivity();
+        if (activity != null) {
+            final long[] rawContactsToRemove = getAdapter().getSelectedContactIdsArray();
+            final Intent intent = ContactSaveService.createGroupUpdateIntent(
+                    activity, mGroupMetadata.groupId, /* groupName */ null,
+                    /* rawContactsToAdd */ null, rawContactsToRemove, activity.getClass(),
+                    GroupMembersActivity.ACTION_SAVE_COMPLETED);
+            activity.startService(intent);
+        }
+    }
+
     private void onGroupMetadataLoaded() {
         final Activity activity = getActivity();
         if (activity != null) activity.invalidateOptionsMenu();
@@ -368,6 +394,14 @@
 
     @Override
     protected void onItemClick(int position, long id) {
+        final Uri uri = getAdapter().getContactUri(position);
+        if (uri == null) {
+            return;
+        }
+        if (getAdapter().isDisplayingCheckBoxes()) {
+            super.onItemClick(position, id);
+            return;
+        }
         if (mCallbacks != null) {
             final Uri contactLookupUri = getAdapter().getContactLookupUri(position);
             mCallbacks.onGroupMemberClicked(contactLookupUri);