Merge "Disable search key press on 1-pane detail/editor activities"
diff --git a/src/com/android/contacts/ContactSaveService.java b/src/com/android/contacts/ContactSaveService.java
index 194d0ba..775009e 100644
--- a/src/com/android/contacts/ContactSaveService.java
+++ b/src/com/android/contacts/ContactSaveService.java
@@ -371,19 +371,29 @@
     }
 
     /**
-     * Creates an intent that can be sent to this service to create a new group.
+     * Creates an intent that can be sent to this service to create a new group as
+     * well as add new members at the same time.
+     *
+     * @param context of the application
+     * @param account in which the group should be created
+     * @param label is the name of the group (cannot be null)
+     * @param rawContactsToAdd is an array of raw contact IDs for contacts that
+     *            should be added to the group
+     * @param callbackActivity is the activity to send the callback intent to
+     * @param callbackAction is the intent action for the callback intent
      */
-    public static Intent createNewGroupIntent(Context context, Account account, String label,
-            Class<?> callbackActivity, String callbackAction) {
+    public static Intent createNewGroupIntent(Context context, Account account,
+            String label, long[] rawContactsToAdd, Class<?> callbackActivity,
+            String callbackAction) {
         Intent serviceIntent = new Intent(context, ContactSaveService.class);
         serviceIntent.setAction(ContactSaveService.ACTION_CREATE_GROUP);
         serviceIntent.putExtra(ContactSaveService.EXTRA_ACCOUNT_TYPE, account.type);
         serviceIntent.putExtra(ContactSaveService.EXTRA_ACCOUNT_NAME, account.name);
         serviceIntent.putExtra(ContactSaveService.EXTRA_GROUP_LABEL, label);
+        serviceIntent.putExtra(ContactSaveService.EXTRA_RAW_CONTACTS_TO_ADD, rawContactsToAdd);
 
         // Callback intent will be invoked by the service once the new group is
-        // created.  The service will put a group membership row in the extras
-        // of the callback intent.
+        // created.
         Intent callbackIntent = new Intent(context, callbackActivity);
         callbackIntent.setAction(callbackAction);
         serviceIntent.putExtra(ContactSaveService.EXTRA_CALLBACK_INTENT, callbackIntent);
@@ -392,28 +402,41 @@
     }
 
     private void createGroup(Intent intent) {
-        String accountType = intent.getStringExtra(EXTRA_ACCOUNT_TYPE);
-        String accountName = intent.getStringExtra(EXTRA_ACCOUNT_NAME);
-        String label = intent.getStringExtra(EXTRA_GROUP_LABEL);
+        final String accountType = intent.getStringExtra(EXTRA_ACCOUNT_TYPE);
+        final String accountName = intent.getStringExtra(EXTRA_ACCOUNT_NAME);
+        final String label = intent.getStringExtra(EXTRA_GROUP_LABEL);
+        final long[] rawContactsToAdd = intent.getLongArrayExtra(EXTRA_RAW_CONTACTS_TO_ADD);
 
         ContentValues values = new ContentValues();
         values.put(Groups.ACCOUNT_TYPE, accountType);
         values.put(Groups.ACCOUNT_NAME, accountName);
         values.put(Groups.TITLE, label);
 
-        Uri groupUri = getContentResolver().insert(Groups.CONTENT_URI, values);
+        final ContentResolver resolver = getContentResolver();
+
+        // Create the new group
+        final Uri groupUri = resolver.insert(Groups.CONTENT_URI, values);
+
+        // If there's no URI, then the insertion failed. Abort early because group members can't be
+        // added if the group doesn't exist
         if (groupUri == null) {
+            Log.e(TAG, "Couldn't create group with label " + label);
             return;
         }
 
+        // Add new group members
+        addMembersToGroup(resolver, rawContactsToAdd, ContentUris.parseId(groupUri));
+
+        // TODO: Move this into the contact editor where it belongs. This needs to be integrated
+        // with the way other intent extras that are passed to the {@link ContactEditorActivity}.
         values.clear();
         values.put(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
         values.put(GroupMembership.GROUP_ROW_ID, ContentUris.parseId(groupUri));
 
         Intent callbackIntent = intent.getParcelableExtra(EXTRA_CALLBACK_INTENT);
         callbackIntent.setData(groupUri);
+        // TODO: This can be taken out when the above TODO is addressed
         callbackIntent.putExtra(ContactsContract.Intents.Insert.DATA, Lists.newArrayList(values));
-
         deliverCallback(callbackIntent);
     }
 
@@ -527,10 +550,23 @@
         if (label != null) {
             ContentValues values = new ContentValues();
             values.put(Groups.TITLE, label);
-            getContentResolver().update(groupUri, values, null, null);
+            resolver.update(groupUri, values, null, null);
         }
 
-        // Add new group members
+        // Add and remove members if necessary
+        addMembersToGroup(resolver, rawContactsToAdd, groupId);
+        removeMembersFromGroup(resolver, rawContactsToRemove, groupId);
+
+        Intent callbackIntent = intent.getParcelableExtra(EXTRA_CALLBACK_INTENT);
+        callbackIntent.setData(groupUri);
+        deliverCallback(callbackIntent);
+    }
+
+    private void addMembersToGroup(ContentResolver resolver, long[] rawContactsToAdd,
+            long groupId) {
+        if (rawContactsToAdd == null) {
+            return;
+        }
         for (long rawContactId : rawContactsToAdd) {
             try {
                 final ArrayList<ContentProviderOperation> rawContactOperations =
@@ -577,8 +613,13 @@
                         String.valueOf(groupId), e);
             }
         }
+    }
 
-        // Remove group members
+    private void removeMembersFromGroup(ContentResolver resolver, long[] rawContactsToRemove,
+            long groupId) {
+        if (rawContactsToRemove == null) {
+            return;
+        }
         for (long rawContactId : rawContactsToRemove) {
             // Apply the delete operation on the data row for the given raw contact's
             // membership in the given group. If no contact matches the provided selection, then
@@ -588,10 +629,6 @@
                     new String[] { String.valueOf(rawContactId),
                     GroupMembership.CONTENT_ITEM_TYPE, String.valueOf(groupId)});
         }
-
-        Intent callbackIntent = intent.getParcelableExtra(EXTRA_CALLBACK_INTENT);
-        callbackIntent.setData(groupUri);
-        deliverCallback(callbackIntent);
     }
 
     /**
diff --git a/src/com/android/contacts/group/GroupEditorFragment.java b/src/com/android/contacts/group/GroupEditorFragment.java
index 76f48a3..da57c82 100644
--- a/src/com/android/contacts/group/GroupEditorFragment.java
+++ b/src/com/android/contacts/group/GroupEditorFragment.java
@@ -238,20 +238,11 @@
                 // Account specified in Intent
                 mAccountName = account.name;
                 mAccountType = account.type;
-                setupAccountHeader();
+                setupEditorForAccount();
             } else {
                 // No Account specified. Let the user choose from a disambiguation dialog.
                 selectAccountAndCreateGroup();
             }
-
-            mStatus = Status.EDITING;
-
-            // The user wants to create a new group, temporarily hide the "add members" text view
-            // TODO: Need to allow users to add members if it's a new group. Under the current
-            // approach, we can't add members because it needs a group ID in order to save,
-            // and we don't have a group ID for a new group until the whole group is saved.
-            // Take this out when batch add/remove members is working.
-            mAutoCompleteTextView.setVisibility(View.GONE);
         } else {
             throw new IllegalArgumentException("Unknown Action String " + mAction +
                     ". Only support " + Intent.ACTION_EDIT + " or " + Intent.ACTION_INSERT);
@@ -278,7 +269,7 @@
         if (accounts.size() == 1) {
             mAccountName = accounts.get(0).name;
             mAccountType = accounts.get(0).type;
-            setupAccountHeader();
+            setupEditorForAccount();
             return;  // Don't show a dialog.
         }
 
@@ -292,7 +283,7 @@
     public void onAccountChosen(int requestCode, Account account) {
         mAccountName = account.name;
         mAccountType = account.type;
-        setupAccountHeader();
+        setupEditorForAccount();
     }
 
     @Override
@@ -304,9 +295,10 @@
     }
 
     /**
-     * Sets up the account header.
+     * Sets up the editor based on the group's account name and type.
      */
-    private void setupAccountHeader() {
+    private void setupEditorForAccount() {
+        // Setup the account header
         final AccountTypeManager accountTypeManager = AccountTypeManager.getInstance(mContext);
         final AccountType accountType = accountTypeManager.getAccountType(mAccountType);
         CharSequence accountTypeDisplayLabel = accountType.getDisplayLabel(mContext);
@@ -316,6 +308,31 @@
         }
         mAccountTypeTextView.setText(accountTypeDisplayLabel);
         mAccountIcon.setImageDrawable(accountType.getDisplayIcon(mContext));
+
+        // Setup the autocomplete adapter (for contacts to suggest to add to the group) based on the
+        // account name and type
+        mAutoCompleteAdapter = new SuggestedMemberListAdapter(mContext,
+                android.R.layout.simple_dropdown_item_1line);
+        mAutoCompleteAdapter.setContentResolver(mContentResolver);
+        mAutoCompleteAdapter.setAccountType(mAccountType);
+        mAutoCompleteAdapter.setAccountName(mAccountName);
+        mAutoCompleteTextView.setAdapter(mAutoCompleteAdapter);
+        mAutoCompleteTextView.setOnItemClickListener(new OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                SuggestedMember member = mAutoCompleteAdapter.getItem(position);
+                loadMemberToAddToGroup(member.getRawContactId(),
+                        String.valueOf(member.getContactId()));
+
+                // Update the autocomplete adapter so the contact doesn't get suggested again
+                mAutoCompleteAdapter.addNewMember(member.getContactId());
+
+                // Clear out the text field
+                mAutoCompleteTextView.setText("");
+            }
+        });
+
+        mStatus = Status.EDITING;
     }
 
     public void load(String action, Uri groupUri, Bundle intentExtras) {
@@ -349,7 +366,7 @@
         // focus on the field).
         mGroupNameView.setText(mOriginalGroupName);
         mGroupNameView.setFocusable(!mGroupNameIsReadOnly);
-        setupAccountHeader();
+        setupEditorForAccount();
     }
 
     public void loadMemberToAddToGroup(long rawContactId, String contactId) {
@@ -380,8 +397,7 @@
     }
 
     private boolean revert() {
-        if (mGroupNameView.getText() != null &&
-                mGroupNameView.getText().toString().equals(mOriginalGroupName)) {
+        if (!hasNameChange() && !hasMembershipChange()) {
             doRevertAction();
         } else {
             CancelEditDialogFragment.show(this);
@@ -453,24 +469,20 @@
         }
         Intent saveIntent = null;
         if (mAction == Intent.ACTION_INSERT) {
-            // TODO: Perform similar work to add members at the same time as creating a new group
+            // Create array of raw contact IDs for contacts to add to the group
+            long[] membersToAddArray = convertToArray(mListMembersToAdd);
+
+            // Create the save intent to create the group and add members at the same time
             saveIntent = ContactSaveService.createNewGroupIntent(activity,
                     new Account(mAccountName, mAccountType), mGroupNameView.getText().toString(),
-                    activity.getClass(), GroupEditorActivity.ACTION_SAVE_COMPLETED);
+                    membersToAddArray, activity.getClass(),
+                    GroupEditorActivity.ACTION_SAVE_COMPLETED);
         } else if (mAction == Intent.ACTION_EDIT) {
             // Create array of raw contact IDs for contacts to add to the group
-            int membersToAddCount = mListMembersToAdd.size();
-            long[] membersToAddArray = new long[membersToAddCount];
-            for (int i = 0; i < membersToAddCount; i++) {
-                membersToAddArray[i] = mListMembersToAdd.get(i).getRawContactId();
-            }
+            long[] membersToAddArray = convertToArray(mListMembersToAdd);
 
             // Create array of raw contact IDs for contacts to add to the group
-            int membersToRemoveCount = mListMembersToRemove.size();
-            long[] membersToRemoveArray = new long[membersToRemoveCount];
-            for (int i = 0; i < membersToRemoveCount; i++) {
-                membersToRemoveArray[i] = mListMembersToRemove.get(i).getRawContactId();
-            }
+            long[] membersToRemoveArray = convertToArray(mListMembersToRemove);
 
             // Create the update intent (which includes the updated group name if necessary)
             saveIntent = ContactSaveService.createGroupUpdateIntent(activity, mGroupId,
@@ -555,6 +567,15 @@
         return groupNameFromTextView;
     }
 
+    private static long[] convertToArray(List<Member> listMembers) {
+        int size = listMembers.size();
+        long[] membersArray = new long[size];
+        for (int i = 0; i < size; i++) {
+            membersArray[i] = listMembers.get(i).getRawContactId();
+        }
+        return membersArray;
+    }
+
     private void addExistingMembers(List<Member> members, List<Long> listContactIds) {
         mListToDisplay.addAll(members);
         mMemberListAdapter.notifyDataSetChanged();
@@ -605,7 +626,6 @@
 
         @Override
         public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
-            mStatus = Status.EDITING;
             bindGroupMetaData(data);
 
             // Load existing members
@@ -651,27 +671,6 @@
                 data.close();
             }
 
-            mAutoCompleteAdapter = new SuggestedMemberListAdapter(getActivity(),
-                    android.R.layout.simple_dropdown_item_1line);
-            mAutoCompleteAdapter.setContentResolver(mContentResolver);
-            mAutoCompleteAdapter.setAccountType(mAccountType);
-            mAutoCompleteAdapter.setAccountName(mAccountName);
-            mAutoCompleteTextView.setAdapter(mAutoCompleteAdapter);
-            mAutoCompleteTextView.setOnItemClickListener(new OnItemClickListener() {
-                @Override
-                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-                    SuggestedMember member = mAutoCompleteAdapter.getItem(position);
-                    loadMemberToAddToGroup(member.getRawContactId(),
-                            String.valueOf(member.getContactId()));
-
-                    // Update the autocomplete adapter so the contact doesn't get suggested again
-                    mAutoCompleteAdapter.addNewMember(member.getContactId());
-
-                    // Clear out the text field
-                    mAutoCompleteTextView.setText("");
-                }
-            });
-
             // Update the display list
             addExistingMembers(listExistingMembers, listContactIds);
 
diff --git a/src/com/android/contacts/interactions/GroupCreationDialogFragment.java b/src/com/android/contacts/interactions/GroupCreationDialogFragment.java
index 8991928..87d83b4 100644
--- a/src/com/android/contacts/interactions/GroupCreationDialogFragment.java
+++ b/src/com/android/contacts/interactions/GroupCreationDialogFragment.java
@@ -60,6 +60,7 @@
         Activity activity = getActivity();
         activity.startService(ContactSaveService.createNewGroupIntent(activity,
                 new Account(accountName, accountType), groupLabel,
+                null /* no new members to add */,
                 activity.getClass(), Intent.ACTION_EDIT));
     }
 }