15ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee/*
25ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Copyright (C) 2009 The Android Open Source Project
35ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee *
45ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Licensed under the Apache License, Version 2.0 (the "License");
55ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * you may not use this file except in compliance with the License.
65ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * You may obtain a copy of the License at
75ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee *
85ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee *      http://www.apache.org/licenses/LICENSE-2.0
95ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee *
105ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Unless required by applicable law or agreed to in writing, software
115ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * distributed under the License is distributed on an "AS IS" BASIS,
125ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * See the License for the specific language governing permissions and
145ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * limitations under the License.
155ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee */
165ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
175ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leepackage com.android.contacts.common.model;
185ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
195ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.content.ContentProviderOperation;
205ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.content.ContentProviderOperation.Builder;
215ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.content.ContentValues;
225ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.content.Context;
235ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.net.Uri;
245ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.os.Parcel;
255ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.os.Parcelable;
265ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.provider.BaseColumns;
275ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.provider.ContactsContract.Data;
285ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.provider.ContactsContract.Profile;
295ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.provider.ContactsContract.RawContacts;
305ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.util.Log;
315ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
325ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport com.android.contacts.common.model.AccountTypeManager;
335ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport com.android.contacts.common.model.ValuesDelta;
345ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport com.android.contacts.common.model.account.AccountType;
355ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport com.android.contacts.common.test.NeededForTesting;
365ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport com.google.common.collect.Lists;
375ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport com.google.common.collect.Maps;
385ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
395ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport java.util.ArrayList;
405ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport java.util.HashMap;
415ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
425ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee/**
435ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Contains a {@link RawContact} and records any modifications separately so the
445ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * original {@link RawContact} can be swapped out with a newer version and the
455ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * changes still cleanly applied.
465ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * <p>
475ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * One benefit of this approach is that we can build changes entirely on an
485ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * empty {@link RawContact}, which then becomes an insert {@link RawContacts} case.
495ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * <p>
505ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * When applying modifications over an {@link RawContact}, we try finding the
515ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * original {@link Data#_ID} rows where the modifications took place. If those
525ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * rows are missing from the new {@link RawContact}, we know the original data must
535ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * be deleted, but to preserve the user modifications we treat as an insert.
545ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee */
555ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leepublic class RawContactDelta implements Parcelable {
565ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    // TODO: optimize by using contentvalues pool, since we allocate so many of them
575ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
585ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    private static final String TAG = "EntityDelta";
595ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    private static final boolean LOGV = false;
605ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
615ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
625ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Direct values from {@link Entity#getEntityValues()}.
635ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
645ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    private ValuesDelta mValues;
655ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
665ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
675ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * URI used for contacts queries, by default it is set to query raw contacts.
685ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * It can be set to query the profile's raw contact(s).
695ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
705ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    private Uri mContactsQueryUri = RawContacts.CONTENT_URI;
715ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
725ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
735ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Internal map of children values from {@link Entity#getSubValues()}, which
745ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * we store here sorted into {@link Data#MIMETYPE} bins.
755ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
765ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    private final HashMap<String, ArrayList<ValuesDelta>> mEntries = Maps.newHashMap();
775ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
785ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public RawContactDelta() {
795ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
805ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
815ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public RawContactDelta(ValuesDelta values) {
825ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        mValues = values;
835ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
845ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
855ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
865ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Build an {@link RawContactDelta} using the given {@link RawContact} as a
875ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * starting point; the "before" snapshot.
885ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
895ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public static RawContactDelta fromBefore(RawContact before) {
905ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final RawContactDelta rawContactDelta = new RawContactDelta();
915ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        rawContactDelta.mValues = ValuesDelta.fromBefore(before.getValues());
925ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        rawContactDelta.mValues.setIdColumn(RawContacts._ID);
935ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (final ContentValues values : before.getContentValues()) {
945ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            rawContactDelta.addEntry(ValuesDelta.fromBefore(values));
955ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
965ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return rawContactDelta;
975ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
985ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
995ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
1005ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Merge the "after" values from the given {@link RawContactDelta} onto the
1015ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * "before" state represented by this {@link RawContactDelta}, discarding any
1025ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * existing "after" states. This is typically used when re-parenting changes
1035ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * onto an updated {@link Entity}.
1045ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
1055ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public static RawContactDelta mergeAfter(RawContactDelta local, RawContactDelta remote) {
1065ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Bail early if trying to merge delete with missing local
1075ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final ValuesDelta remoteValues = remote.mValues;
1085ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (local == null && (remoteValues.isDelete() || remoteValues.isTransient())) return null;
1095ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
1105ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Create local version if none exists yet
1115ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (local == null) local = new RawContactDelta();
1125ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
1135ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (LOGV) {
1145ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final Long localVersion = (local.mValues == null) ? null : local.mValues
1155ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    .getAsLong(RawContacts.VERSION);
1165ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final Long remoteVersion = remote.mValues.getAsLong(RawContacts.VERSION);
1175ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            Log.d(TAG, "Re-parenting from original version " + remoteVersion + " to "
1185ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    + localVersion);
1195ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
1205ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
1215ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Create values if needed, and merge "after" changes
1225ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        local.mValues = ValuesDelta.mergeAfter(local.mValues, remote.mValues);
1235ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
1245ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Find matching local entry for each remote values, or create
1255ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (ArrayList<ValuesDelta> mimeEntries : remote.mEntries.values()) {
1265ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            for (ValuesDelta remoteEntry : mimeEntries) {
1275ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                final Long childId = remoteEntry.getId();
1285ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
1295ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                // Find or create local match and merge
1305ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                final ValuesDelta localEntry = local.getEntry(childId);
1315ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                final ValuesDelta merged = ValuesDelta.mergeAfter(localEntry, remoteEntry);
1325ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
1335ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (localEntry == null && merged != null) {
1345ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    // No local entry before, so insert
1355ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    local.addEntry(merged);
1365ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
1375ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
1385ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
1395ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
1405ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return local;
1415ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
1425ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
1435ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public ValuesDelta getValues() {
1445ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return mValues;
1455ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
1465ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
1475ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public boolean isContactInsert() {
1485ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return mValues.isInsert();
1495ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
1505ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
1515ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
1525ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Get the {@link ValuesDelta} child marked as {@link Data#IS_PRIMARY},
1535ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * which may return null when no entry exists.
1545ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
1555ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public ValuesDelta getPrimaryEntry(String mimeType) {
1565ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final ArrayList<ValuesDelta> mimeEntries = getMimeEntries(mimeType, false);
1575ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (mimeEntries == null) return null;
1585ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
1595ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (ValuesDelta entry : mimeEntries) {
1605ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (entry.isPrimary()) {
1615ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                return entry;
1625ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
1635ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
1645ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
1655ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // When no direct primary, return something
1665ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return mimeEntries.size() > 0 ? mimeEntries.get(0) : null;
1675ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
1685ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
1695ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
1705ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * calls {@link #getSuperPrimaryEntry(String, boolean)} with true
1715ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * @see #getSuperPrimaryEntry(String, boolean)
1725ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
1735ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public ValuesDelta getSuperPrimaryEntry(String mimeType) {
1745ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return getSuperPrimaryEntry(mimeType, true);
1755ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
1765ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
1775ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
1785ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Returns the super-primary entry for the given mime type
1795ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * @param forceSelection if true, will try to return some value even if a super-primary
1805ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     *     doesn't exist (may be a primary, or just a random item
1815ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * @return
1825ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
1835ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    @NeededForTesting
1845ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public ValuesDelta getSuperPrimaryEntry(String mimeType, boolean forceSelection) {
1855ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final ArrayList<ValuesDelta> mimeEntries = getMimeEntries(mimeType, false);
1865ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (mimeEntries == null) return null;
1875ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
1885ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        ValuesDelta primary = null;
1895ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (ValuesDelta entry : mimeEntries) {
1905ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (entry.isSuperPrimary()) {
1915ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                return entry;
1925ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            } else if (entry.isPrimary()) {
1935ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                primary = entry;
1945ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
1955ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
1965ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
1975ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (!forceSelection) {
1985ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            return null;
1995ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
2005ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
2015ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // When no direct super primary, return something
2025ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (primary != null) {
2035ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            return primary;
2045ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
2055ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return mimeEntries.size() > 0 ? mimeEntries.get(0) : null;
2065ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
2075ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
2085ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
2095ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Return the AccountType that this raw-contact belongs to.
2105ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
2115ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public AccountType getRawContactAccountType(Context context) {
2125ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        ContentValues entityValues = getValues().getCompleteValues();
2135ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        String type = entityValues.getAsString(RawContacts.ACCOUNT_TYPE);
2145ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        String dataSet = entityValues.getAsString(RawContacts.DATA_SET);
2155ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return AccountTypeManager.getInstance(context).getAccountType(type, dataSet);
2165ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
2175ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
2185ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public Long getRawContactId() {
2195ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return getValues().getAsLong(RawContacts._ID);
2205ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
2215ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
2225ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public String getAccountName() {
2235ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return getValues().getAsString(RawContacts.ACCOUNT_NAME);
2245ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
2255ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
2265ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public String getAccountType() {
2275ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return getValues().getAsString(RawContacts.ACCOUNT_TYPE);
2285ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
2295ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
2305ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public String getDataSet() {
2315ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return getValues().getAsString(RawContacts.DATA_SET);
2325ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
2335ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
2345ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public AccountType getAccountType(AccountTypeManager manager) {
2355ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return manager.getAccountType(getAccountType(), getDataSet());
2365ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
2375ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
2385ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public boolean isVisible() {
2395ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return getValues().isVisible();
2405ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
2415ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
2425ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
2435ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Return the list of child {@link ValuesDelta} from our optimized map,
2445ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * creating the list if requested.
2455ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
2465ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    private ArrayList<ValuesDelta> getMimeEntries(String mimeType, boolean lazyCreate) {
2475ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        ArrayList<ValuesDelta> mimeEntries = mEntries.get(mimeType);
2485ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (mimeEntries == null && lazyCreate) {
2495ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            mimeEntries = Lists.newArrayList();
2505ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            mEntries.put(mimeType, mimeEntries);
2515ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
2525ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return mimeEntries;
2535ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
2545ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
2555ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public ArrayList<ValuesDelta> getMimeEntries(String mimeType) {
2565ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return getMimeEntries(mimeType, false);
2575ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
2585ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
2595ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public int getMimeEntriesCount(String mimeType, boolean onlyVisible) {
2605ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final ArrayList<ValuesDelta> mimeEntries = getMimeEntries(mimeType);
2615ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (mimeEntries == null) return 0;
2625ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
2635ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        int count = 0;
2645ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (ValuesDelta child : mimeEntries) {
2655ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            // Skip deleted items when requesting only visible
2665ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (onlyVisible && !child.isVisible()) continue;
2675ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            count++;
2685ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
2695ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return count;
2705ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
2715ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
2725ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public boolean hasMimeEntries(String mimeType) {
2735ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return mEntries.containsKey(mimeType);
2745ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
2755ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
2765ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public ValuesDelta addEntry(ValuesDelta entry) {
2775ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final String mimeType = entry.getMimetype();
2785ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        getMimeEntries(mimeType, true).add(entry);
2795ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return entry;
2805ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
2815ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
2825ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public ArrayList<ContentValues> getContentValues() {
2835ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        ArrayList<ContentValues> values = Lists.newArrayList();
2845ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (ArrayList<ValuesDelta> mimeEntries : mEntries.values()) {
2855ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            for (ValuesDelta entry : mimeEntries) {
2865ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (!entry.isDelete()) {
2875ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    values.add(entry.getCompleteValues());
2885ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
2895ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
2905ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
2915ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return values;
2925ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
2935ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
2945ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
2955ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Find entry with the given {@link BaseColumns#_ID} value.
2965ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
2975ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public ValuesDelta getEntry(Long childId) {
2985ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (childId == null) {
2995ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            // Requesting an "insert" entry, which has no "before"
3005ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            return null;
3015ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
3025ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
3035ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Search all children for requested entry
3045ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (ArrayList<ValuesDelta> mimeEntries : mEntries.values()) {
3055ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            for (ValuesDelta entry : mimeEntries) {
3065ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (childId.equals(entry.getId())) {
3075ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    return entry;
3085ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
3095ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
3105ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
3115ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return null;
3125ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
3135ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
3145ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
3155ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Return the total number of {@link ValuesDelta} contained.
3165ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
3175ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public int getEntryCount(boolean onlyVisible) {
3185ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        int count = 0;
3195ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (String mimeType : mEntries.keySet()) {
3205ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            count += getMimeEntriesCount(mimeType, onlyVisible);
3215ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
3225ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return count;
3235ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
3245ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
3255ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    @Override
3265ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public boolean equals(Object object) {
3275ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (object instanceof RawContactDelta) {
3285ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final RawContactDelta other = (RawContactDelta)object;
3295ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
3305ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            // Equality failed if parent values different
3315ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (!other.mValues.equals(mValues)) return false;
3325ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
3335ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            for (ArrayList<ValuesDelta> mimeEntries : mEntries.values()) {
3345ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                for (ValuesDelta child : mimeEntries) {
3355ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    // Equality failed if any children unmatched
3365ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    if (!other.containsEntry(child)) return false;
3375ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
3385ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
3395ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
3405ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            // Passed all tests, so equal
3415ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            return true;
3425ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
3435ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return false;
3445ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
3455ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
3465ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    private boolean containsEntry(ValuesDelta entry) {
3475ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (ArrayList<ValuesDelta> mimeEntries : mEntries.values()) {
3485ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            for (ValuesDelta child : mimeEntries) {
3495ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                // Contained if we find any child that matches
3505ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (child.equals(entry)) return true;
3515ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
3525ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
3535ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return false;
3545ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
3555ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
3565ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
3575ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Mark this entire object deleted, including any {@link ValuesDelta}.
3585ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
3595ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public void markDeleted() {
3605ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        this.mValues.markDeleted();
3615ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (ArrayList<ValuesDelta> mimeEntries : mEntries.values()) {
3625ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            for (ValuesDelta child : mimeEntries) {
3635ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                child.markDeleted();
3645ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
3655ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
3665ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
3675ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
3685ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    @Override
3695ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public String toString() {
3705ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final StringBuilder builder = new StringBuilder();
3715ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        builder.append("\n(");
3725ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        builder.append("Uri=");
3735ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        builder.append(mContactsQueryUri);
3745ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        builder.append(", Values=");
3755ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        builder.append(mValues != null ? mValues.toString() : "null");
3765ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        builder.append(", Entries={");
3775ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (ArrayList<ValuesDelta> mimeEntries : mEntries.values()) {
3785ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            for (ValuesDelta child : mimeEntries) {
3795ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                builder.append("\n\t");
3805ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                child.toString(builder);
3815ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
3825ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
3835ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        builder.append("\n})\n");
3845ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return builder.toString();
3855ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
3865ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
3875ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
3885ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Consider building the given {@link ContentProviderOperation.Builder} and
3895ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * appending it to the given list, which only happens if builder is valid.
3905ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
3915ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    private void possibleAdd(ArrayList<ContentProviderOperation> diff,
3925ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            ContentProviderOperation.Builder builder) {
3935ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (builder != null) {
3945ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            diff.add(builder.build());
3955ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
3965ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
3975ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
3985ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
3995ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Build a list of {@link ContentProviderOperation} that will assert any
4005ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * "before" state hasn't changed. This is maintained separately so that all
4015ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * asserts can take place before any updates occur.
4025ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
4035ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public void buildAssert(ArrayList<ContentProviderOperation> buildInto) {
4045ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final boolean isContactInsert = mValues.isInsert();
4055ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (!isContactInsert) {
4065ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            // Assert version is consistent while persisting changes
4075ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final Long beforeId = mValues.getId();
4085ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final Long beforeVersion = mValues.getAsLong(RawContacts.VERSION);
4095ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (beforeId == null || beforeVersion == null) return;
4105ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
4115ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final ContentProviderOperation.Builder builder = ContentProviderOperation
4125ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    .newAssertQuery(mContactsQueryUri);
4135ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            builder.withSelection(RawContacts._ID + "=" + beforeId, null);
4145ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            builder.withValue(RawContacts.VERSION, beforeVersion);
4155ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            buildInto.add(builder.build());
4165ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
4175ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
4185ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
4195ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
4205ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Build a list of {@link ContentProviderOperation} that will transform the
4215ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * current "before" {@link Entity} state into the modified state which this
4225ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * {@link RawContactDelta} represents.
4235ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
4245ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public void buildDiff(ArrayList<ContentProviderOperation> buildInto) {
4255ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final int firstIndex = buildInto.size();
4265ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
4275ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final boolean isContactInsert = mValues.isInsert();
4285ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final boolean isContactDelete = mValues.isDelete();
4295ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final boolean isContactUpdate = !isContactInsert && !isContactDelete;
4305ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
4315ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final Long beforeId = mValues.getId();
4325ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
4335ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        Builder builder;
4345ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
4355ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (isContactInsert) {
4365ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            // TODO: for now simply disabling aggregation when a new contact is
4375ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            // created on the phone.  In the future, will show aggregation suggestions
4385ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            // after saving the contact.
4395ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            mValues.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED);
4405ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
4415ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
4425ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Build possible operation at Contact level
4435ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        builder = mValues.buildDiff(mContactsQueryUri);
4445ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        possibleAdd(buildInto, builder);
4455ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
4465ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Build operations for all children
4475ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (ArrayList<ValuesDelta> mimeEntries : mEntries.values()) {
4485ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            for (ValuesDelta child : mimeEntries) {
4495ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                // Ignore children if parent was deleted
4505ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (isContactDelete) continue;
4515ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
4525ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                // Use the profile data URI if the contact is the profile.
4535ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (mContactsQueryUri.equals(Profile.CONTENT_RAW_CONTACTS_URI)) {
4545ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    builder = child.buildDiff(Uri.withAppendedPath(Profile.CONTENT_URI,
4555ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                            RawContacts.Data.CONTENT_DIRECTORY));
4565ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                } else {
4575ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    builder = child.buildDiff(Data.CONTENT_URI);
4585ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
4595ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
4605ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (child.isInsert()) {
4615ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    if (isContactInsert) {
4625ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        // Parent is brand new insert, so back-reference _id
4635ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        builder.withValueBackReference(Data.RAW_CONTACT_ID, firstIndex);
4645ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    } else {
4655ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        // Inserting under existing, so fill with known _id
4665ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        builder.withValue(Data.RAW_CONTACT_ID, beforeId);
4675ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    }
4685ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                } else if (isContactInsert && builder != null) {
4695ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    // Child must be insert when Contact insert
4705ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    throw new IllegalArgumentException("When parent insert, child must be also");
4715ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
4725ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                possibleAdd(buildInto, builder);
4735ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
4745ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
4755ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
4765ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final boolean addedOperations = buildInto.size() > firstIndex;
4775ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (addedOperations && isContactUpdate) {
4785ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            // Suspend aggregation while persisting updates
4795ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            builder = buildSetAggregationMode(beforeId, RawContacts.AGGREGATION_MODE_SUSPENDED);
4805ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            buildInto.add(firstIndex, builder.build());
4815ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
4825ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            // Restore aggregation mode as last operation
4835ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            builder = buildSetAggregationMode(beforeId, RawContacts.AGGREGATION_MODE_DEFAULT);
4845ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            buildInto.add(builder.build());
4855ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        } else if (isContactInsert) {
4865ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            // Restore aggregation mode as last operation
4875ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            builder = ContentProviderOperation.newUpdate(mContactsQueryUri);
4885ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            builder.withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DEFAULT);
4895ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            builder.withSelection(RawContacts._ID + "=?", new String[1]);
4905ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            builder.withSelectionBackReference(0, firstIndex);
4915ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            buildInto.add(builder.build());
4925ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
4935ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
4945ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
4955ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
4965ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Build a {@link ContentProviderOperation} that changes
4975ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * {@link RawContacts#AGGREGATION_MODE} to the given value.
4985ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
4995ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    protected Builder buildSetAggregationMode(Long beforeId, int mode) {
5005ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        Builder builder = ContentProviderOperation.newUpdate(mContactsQueryUri);
5015ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        builder.withValue(RawContacts.AGGREGATION_MODE, mode);
5025ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        builder.withSelection(RawContacts._ID + "=" + beforeId, null);
5035ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return builder;
5045ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
5055ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
5065ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /** {@inheritDoc} */
5075ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public int describeContents() {
5085ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Nothing special about this parcel
5095ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return 0;
5105ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
5115ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
5125ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /** {@inheritDoc} */
5135ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public void writeToParcel(Parcel dest, int flags) {
5145ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final int size = this.getEntryCount(false);
5155ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        dest.writeInt(size);
5165ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        dest.writeParcelable(mValues, flags);
5175ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        dest.writeParcelable(mContactsQueryUri, flags);
5185ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (ArrayList<ValuesDelta> mimeEntries : mEntries.values()) {
5195ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            for (ValuesDelta child : mimeEntries) {
5205ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                dest.writeParcelable(child, flags);
5215ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
5225ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
5235ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
5245ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
5255ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public void readFromParcel(Parcel source) {
5265ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final ClassLoader loader = getClass().getClassLoader();
5275ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final int size = source.readInt();
5285ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        mValues = source.<ValuesDelta> readParcelable(loader);
5295ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        mContactsQueryUri = source.<Uri> readParcelable(loader);
5305ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (int i = 0; i < size; i++) {
5315ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final ValuesDelta child = source.<ValuesDelta> readParcelable(loader);
5325ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            this.addEntry(child);
5335ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
5345ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
5355ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
5365ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
5375ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Used to set the query URI to the profile URI to store profiles.
5385ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
5395ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public void setProfileQueryUri() {
5405ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        mContactsQueryUri = Profile.CONTENT_RAW_CONTACTS_URI;
5415ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
5425ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
5435ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public static final Parcelable.Creator<RawContactDelta> CREATOR =
5445ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            new Parcelable.Creator<RawContactDelta>() {
5455ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        public RawContactDelta createFromParcel(Parcel in) {
5465ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final RawContactDelta state = new RawContactDelta();
5475ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            state.readFromParcel(in);
5485ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            return state;
5495ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
5505ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
5515ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        public RawContactDelta[] newArray(int size) {
5525ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            return new RawContactDelta[size];
5535ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
5545ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    };
5555ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
5565ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee}
557