Batch add and remove group members in group editor
- Create save intent in ContactSaveService to update
the group name, add a list of new members, and remove
a list of existing members
- Add raw contact ID column to GroupMemberLoader so that
the raw contact IDs can be used in ContactSaveService
- Remove old code that would do the database save one at a
time for each contact member when the action was taken
(and remove the step that would load the full contact because
we now only need the raw contact ID)
- TODO: General cleanup to just use raw contact IDs everywhere
and have one Member class (get rid of SuggestedMember)
- TODO: Allow users to add contacts for new groups (still
can only change the name), but this should be straightforward
after this change
Bug: 4961845
Change-Id: I8a2f1086feecbb63dc6eb3d1e985bccabe28b803
diff --git a/src/com/android/contacts/ContactSaveService.java b/src/com/android/contacts/ContactSaveService.java
index 5912225..194d0ba 100644
--- a/src/com/android/contacts/ContactSaveService.java
+++ b/src/com/android/contacts/ContactSaveService.java
@@ -78,8 +78,11 @@
public static final String ACTION_CREATE_GROUP = "createGroup";
public static final String ACTION_RENAME_GROUP = "renameGroup";
public static final String ACTION_DELETE_GROUP = "deleteGroup";
+ public static final String ACTION_UPDATE_GROUP = "updateGroup";
public static final String EXTRA_GROUP_ID = "groupId";
public static final String EXTRA_GROUP_LABEL = "groupLabel";
+ public static final String EXTRA_RAW_CONTACTS_TO_ADD = "rawContactsToAdd";
+ public static final String EXTRA_RAW_CONTACTS_TO_REMOVE = "rawContactsToRemove";
public static final String ACTION_SET_STARRED = "setStarred";
public static final String ACTION_DELETE_CONTACT = "delete";
@@ -170,6 +173,8 @@
renameGroup(intent);
} else if (ACTION_DELETE_GROUP.equals(action)) {
deleteGroup(intent);
+ } else if (ACTION_UPDATE_GROUP.equals(action)) {
+ updateGroup(intent);
} else if (ACTION_SET_STARRED.equals(action)) {
setStarred(intent);
} else if (ACTION_SET_SUPER_PRIMARY.equals(action)) {
@@ -471,6 +476,125 @@
}
/**
+ * Creates an intent that can be sent to this service to rename a group as
+ * well as add and remove members from the group.
+ *
+ * @param context of the application
+ * @param groupId of the group that should be modified
+ * @param newLabel is the updated name of the group (can be null if the name
+ * should not be updated)
+ * @param rawContactsToAdd is an array of raw contact IDs for contacts that
+ * should be added to the group
+ * @param rawContactsToRemove is an array of raw contact IDs for contacts
+ * that should be removed from 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 createGroupUpdateIntent(Context context, long groupId, String newLabel,
+ long[] rawContactsToAdd, long[] rawContactsToRemove,
+ Class<?> callbackActivity, String callbackAction) {
+ Intent serviceIntent = new Intent(context, ContactSaveService.class);
+ serviceIntent.setAction(ContactSaveService.ACTION_UPDATE_GROUP);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_GROUP_ID, groupId);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_GROUP_LABEL, newLabel);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_RAW_CONTACTS_TO_ADD, rawContactsToAdd);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_RAW_CONTACTS_TO_REMOVE,
+ rawContactsToRemove);
+
+ // Callback intent will be invoked by the service once the group is updated
+ Intent callbackIntent = new Intent(context, callbackActivity);
+ callbackIntent.setAction(callbackAction);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_CALLBACK_INTENT, callbackIntent);
+
+ return serviceIntent;
+ }
+
+ private void updateGroup(Intent intent) {
+ long groupId = intent.getLongExtra(EXTRA_GROUP_ID, -1);
+ String label = intent.getStringExtra(EXTRA_GROUP_LABEL);
+ long[] rawContactsToAdd = intent.getLongArrayExtra(EXTRA_RAW_CONTACTS_TO_ADD);
+ long[] rawContactsToRemove = intent.getLongArrayExtra(EXTRA_RAW_CONTACTS_TO_REMOVE);
+
+ if (groupId == -1) {
+ Log.e(TAG, "Invalid arguments for updateGroup request");
+ return;
+ }
+
+ final ContentResolver resolver = getContentResolver();
+ final Uri groupUri = ContentUris.withAppendedId(Groups.CONTENT_URI, groupId);
+
+ // Update group name if necessary
+ if (label != null) {
+ ContentValues values = new ContentValues();
+ values.put(Groups.TITLE, label);
+ getContentResolver().update(groupUri, values, null, null);
+ }
+
+ // Add new group members
+ for (long rawContactId : rawContactsToAdd) {
+ try {
+ final ArrayList<ContentProviderOperation> rawContactOperations =
+ new ArrayList<ContentProviderOperation>();
+
+ // Build an assert operation to ensure the contact is not already in the group
+ final ContentProviderOperation.Builder assertBuilder = ContentProviderOperation
+ .newAssertQuery(Data.CONTENT_URI);
+ assertBuilder.withSelection(Data.RAW_CONTACT_ID + "=? AND " +
+ Data.MIMETYPE + "=? AND " + GroupMembership.GROUP_ROW_ID + "=?",
+ new String[] { String.valueOf(rawContactId),
+ GroupMembership.CONTENT_ITEM_TYPE, String.valueOf(groupId)});
+ assertBuilder.withExpectedCount(0);
+ rawContactOperations.add(assertBuilder.build());
+
+ // Build an insert operation to add the contact to the group
+ final ContentProviderOperation.Builder insertBuilder = ContentProviderOperation
+ .newInsert(Data.CONTENT_URI);
+ insertBuilder.withValue(Data.RAW_CONTACT_ID, rawContactId);
+ insertBuilder.withValue(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
+ insertBuilder.withValue(GroupMembership.GROUP_ROW_ID, groupId);
+ rawContactOperations.add(insertBuilder.build());
+
+ if (DEBUG) {
+ for (ContentProviderOperation operation : rawContactOperations) {
+ Log.v(TAG, operation.toString());
+ }
+ }
+
+ // Apply batch
+ ContentProviderResult[] results = null;
+ if (!rawContactOperations.isEmpty()) {
+ results = resolver.applyBatch(ContactsContract.AUTHORITY, rawContactOperations);
+ }
+ } catch (RemoteException e) {
+ // Something went wrong, bail without success
+ Log.e(TAG, "Problem persisting user edits for raw contact ID " +
+ String.valueOf(rawContactId), e);
+ } catch (OperationApplicationException e) {
+ // The assert could have failed because the contact is already in the group,
+ // just continue to the next contact
+ Log.w(TAG, "Assert failed in adding raw contact ID " +
+ String.valueOf(rawContactId) + ". Already exists in group " +
+ String.valueOf(groupId), e);
+ }
+ }
+
+ // Remove group members
+ 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
+ // nothing will be done. Just continue to the next contact.
+ getContentResolver().delete(Data.CONTENT_URI, Data.RAW_CONTACT_ID + "=? AND " +
+ Data.MIMETYPE + "=? AND " + GroupMembership.GROUP_ROW_ID + "=?",
+ new String[] { String.valueOf(rawContactId),
+ GroupMembership.CONTENT_ITEM_TYPE, String.valueOf(groupId)});
+ }
+
+ Intent callbackIntent = intent.getParcelableExtra(EXTRA_CALLBACK_INTENT);
+ callbackIntent.setData(groupUri);
+ deliverCallback(callbackIntent);
+ }
+
+ /**
* Creates an intent that can be sent to this service to star or un-star a contact.
*/
public static Intent createSetStarredIntent(Context context, Uri contactUri, boolean value) {