private Builder buildAssertHelper() { final boolean isContactInsert = mValues.isInsert(); ContentProviderOperation.Builder builder = null; if (!isContactInsert) { // Assert version is consistent while persisting changes final Long beforeId = mValues.getId(); final Long beforeVersion = mValues.getAsLong(RawContacts.VERSION); if (beforeId == null || beforeVersion == null) return builder; builder = ContentProviderOperation.newAssertQuery(mContactsQueryUri); builder.withSelection(RawContacts._ID + "=" + beforeId, null); builder.withValue(RawContacts.VERSION, beforeVersion); } return builder; }
/** Find entry with the given {@link BaseColumns#_ID} value. */ public ValuesDelta getEntry(Long childId) { if (childId == null) { // Requesting an "insert" entry, which has no "before" return null; } // Search all children for requested entry for (ArrayList<ValuesDelta> mimeEntries : mEntries.values()) { for (ValuesDelta entry : mimeEntries) { if (childId.equals(entry.getId())) { return entry; } } } return null; }
private static String getMapKey( RawContactDelta entity, DataKind kind, ValuesDelta values, int viewIndex) { sWorkStringBuilder.setLength(0); if (entity != null) { sWorkStringBuilder.append(entity.getValues().getId()); if (kind != null) { sWorkStringBuilder.append(KEY_SEPARATOR); sWorkStringBuilder.append(kind.mimeType); if (values != null) { sWorkStringBuilder.append(KEY_SEPARATOR); sWorkStringBuilder.append(values.getId()); if (viewIndex != NO_VIEW_INDEX) { sWorkStringBuilder.append(KEY_SEPARATOR); sWorkStringBuilder.append(viewIndex); } } } } return sWorkStringBuilder.toString(); }
/** * 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)); } }