public ArrayList<ContentValues> getContentValues() {
   ArrayList<ContentValues> values = Lists.newArrayList();
   for (ArrayList<ValuesDelta> mimeEntries : mEntries.values()) {
     for (ValuesDelta entry : mimeEntries) {
       if (!entry.isDelete()) {
         values.add(entry.getCompleteValues());
       }
     }
   }
   return values;
 }
  private boolean hasMembership(long groupId) {
    if (groupId == mDefaultGroupId && mState.isContactInsert()) {
      return true;
    }

    ArrayList<ValuesDelta> entries = mState.getMimeEntries(GroupMembership.CONTENT_ITEM_TYPE);
    if (entries != null) {
      for (ValuesDelta values : entries) {
        if (!values.isDelete()) {
          Long id = values.getGroupRowId();
          if (id != null && id == groupId) {
            return true;
          }
        }
      }
    }
    return false;
  }
  @Override
  public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    ListView list = (ListView) parent;
    int count = mAdapter.getCount();

    if (list.isItemChecked(count - 1)) {
      list.setItemChecked(count - 1, false);
      createNewGroup();
      return;
    }

    for (int i = 0; i < count; i++) {
      mAdapter.getItem(i).setChecked(list.isItemChecked(i));
    }

    // First remove the memberships that have been unchecked
    ArrayList<ValuesDelta> entries = mState.getMimeEntries(GroupMembership.CONTENT_ITEM_TYPE);
    if (entries != null) {
      for (ValuesDelta entry : entries) {
        if (!entry.isDelete()) {
          Long groupId = entry.getGroupRowId();
          if (groupId != null
              && groupId != mFavoritesGroupId
              && (groupId != mDefaultGroupId || mDefaultGroupVisible)
              && !isGroupChecked(groupId)) {
            entry.markDeleted();
          }
        }
      }
    }

    // Now add the newly selected items
    for (int i = 0; i < count; i++) {
      GroupSelectionItem item = mAdapter.getItem(i);
      long groupId = item.getGroupId();
      if (item.isChecked() && !hasMembership(groupId)) {
        ValuesDelta entry = RawContactModifier.insertChild(mState, mKind);
        entry.setGroupRowId(groupId);
      }
    }

    updateView();
  }
  /**
   * Merge the "after" values from the given {@link RawContactDelta} onto the "before" state
   * represented by this {@link RawContactDelta}, discarding any existing "after" states. This is
   * typically used when re-parenting changes onto an updated {@link Entity}.
   */
  public static RawContactDelta mergeAfter(RawContactDelta local, RawContactDelta remote) {
    // Bail early if trying to merge delete with missing local
    final ValuesDelta remoteValues = remote.mValues;
    if (local == null && (remoteValues.isDelete() || remoteValues.isTransient())) return null;

    // Create local version if none exists yet
    if (local == null) local = new RawContactDelta();

    if (LOGV) {
      final Long localVersion =
          (local.mValues == null) ? null : local.mValues.getAsLong(RawContacts.VERSION);
      final Long remoteVersion = remote.mValues.getAsLong(RawContacts.VERSION);
      Log.d(TAG, "Re-parenting from original version " + remoteVersion + " to " + localVersion);
    }

    // Create values if needed, and merge "after" changes
    local.mValues = ValuesDelta.mergeAfter(local.mValues, remote.mValues);

    // Find matching local entry for each remote values, or create
    for (ArrayList<ValuesDelta> mimeEntries : remote.mEntries.values()) {
      for (ValuesDelta remoteEntry : mimeEntries) {
        final Long childId = remoteEntry.getId();

        // Find or create local match and merge
        final ValuesDelta localEntry = local.getEntry(childId);
        final ValuesDelta merged = ValuesDelta.mergeAfter(localEntry, remoteEntry);

        if (localEntry == null && merged != null) {
          // No local entry before, so insert
          local.addEntry(merged);
        }
      }
    }

    return local;
  }
  /**
   * For compatibility purpose, this method is copied from {@link #buildDiff} and takes an ArrayList
   * of CPOWrapper as parameter.
   */
  public void buildDiffWrapper(ArrayList<CPOWrapper> buildInto) {
    final int firstIndex = buildInto.size();

    final boolean isContactInsert = mValues.isInsert();
    final boolean isContactDelete = mValues.isDelete();
    final boolean isContactUpdate = !isContactInsert && !isContactDelete;

    final Long beforeId = mValues.getId();

    if (isContactInsert) {
      // TODO: for now simply disabling aggregation when a new contact is
      // created on the phone.  In the future, will show aggregation suggestions
      // after saving the contact.
      mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED);
    }

    // Build possible operation at Contact level
    BuilderWrapper bw = mValues.buildDiffWrapper(mContactsQueryUri);
    possibleAddWrapper(buildInto, bw);

    // Build operations for all children
    for (ArrayList<ValuesDelta> mimeEntries : mEntries.values()) {
      for (ValuesDelta child : mimeEntries) {
        // Ignore children if parent was deleted
        if (isContactDelete) continue;

        // Use the profile data URI if the contact is the profile.
        if (mContactsQueryUri.equals(Profile.CONTENT_RAW_CONTACTS_URI)) {
          bw =
              child.buildDiffWrapper(
                  Uri.withAppendedPath(Profile.CONTENT_URI, RawContacts.Data.CONTENT_DIRECTORY));
        } else {
          bw = child.buildDiffWrapper(Data.CONTENT_URI);
        }

        if (child.isInsert()) {
          if (isContactInsert) {
            // Parent is brand new insert, so back-reference _id
            bw.getBuilder().withValueBackReference(Data.RAW_CONTACT_ID, firstIndex);
          } else {
            // Inserting under existing, so fill with known _id
            bw.getBuilder().withValue(Data.RAW_CONTACT_ID, beforeId);
          }
        } else if (isContactInsert && bw != null && bw.getBuilder() != null) {
          // Child must be insert when Contact insert
          throw new IllegalArgumentException("When parent insert, child must be also");
        }
        possibleAddWrapper(buildInto, bw);
      }
    }

    final boolean addedOperations = buildInto.size() > firstIndex;
    if (addedOperations && isContactUpdate) {
      // Suspend aggregation while persisting updates
      Builder builder = buildSetAggregationMode(beforeId, RawContacts.AGGREGATION_MODE_SUSPENDED);
      buildInto.add(firstIndex, new CPOWrapper(builder.build(), CompatUtils.TYPE_UPDATE));

      // Restore aggregation mode as last operation
      builder = buildSetAggregationMode(beforeId, RawContacts.AGGREGATION_MODE_DEFAULT);
      buildInto.add(new CPOWrapper(builder.build(), CompatUtils.TYPE_UPDATE));
    } else if (isContactInsert) {
      // Restore aggregation mode as last operation
      Builder builder = ContentProviderOperation.newUpdate(mContactsQueryUri);
      builder.withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DEFAULT);
      builder.withSelection(RawContacts._ID + "=?", new String[1]);
      builder.withSelectionBackReference(0, firstIndex);
      buildInto.add(new CPOWrapper(builder.build(), CompatUtils.TYPE_UPDATE));
    }
  }