Prefactor RawContactDelta.ValuesDelta into top level class.

Making ValuesDelta into top level class so we do not have to move
RawContactDelta into ContactsCommon. ValuesDelta is used by
CustomContactListFilterActivity which needs to be moved.

Bug: 6993891
Change-Id: If7371cf08ac0e14218fb790c96856e971fa613ec
diff --git a/src/com/android/contacts/activities/AttachPhotoActivity.java b/src/com/android/contacts/activities/AttachPhotoActivity.java
index 54f2de8..1eb77dd 100644
--- a/src/com/android/contacts/activities/AttachPhotoActivity.java
+++ b/src/com/android/contacts/activities/AttachPhotoActivity.java
@@ -39,6 +39,7 @@
 import com.android.contacts.model.RawContactDeltaList;
 import com.android.contacts.model.RawContactModifier;
 import com.android.contacts.common.model.account.AccountType;
+import com.android.contacts.model.ValuesDelta;
 import com.android.contacts.util.ContactPhotoUtils;
 
 import java.io.File;
@@ -195,7 +196,7 @@
         // the ContactSaveService would not create the new contact, and the
         // full-res photo would fail to be saved to the non-existent contact.
         AccountType account = raw.getRawContactAccountType(this);
-        RawContactDelta.ValuesDelta values =
+        ValuesDelta values =
                 RawContactModifier.ensureKindExists(raw, account, Photo.CONTENT_ITEM_TYPE);
         if (values == null) {
             Log.w(TAG, "cannot attach photo to this account type");
diff --git a/src/com/android/contacts/activities/ConfirmAddDetailActivity.java b/src/com/android/contacts/activities/ConfirmAddDetailActivity.java
index f90ae7f..99301c1 100644
--- a/src/com/android/contacts/activities/ConfirmAddDetailActivity.java
+++ b/src/com/android/contacts/activities/ConfirmAddDetailActivity.java
@@ -64,7 +64,7 @@
 import com.android.contacts.common.model.AccountTypeManager;
 import com.android.contacts.model.RawContact;
 import com.android.contacts.model.RawContactDelta;
-import com.android.contacts.model.RawContactDelta.ValuesDelta;
+import com.android.contacts.model.ValuesDelta;
 import com.android.contacts.model.RawContactDeltaList;
 import com.android.contacts.model.RawContactModifier;
 import com.android.contacts.common.model.account.AccountType;
diff --git a/src/com/android/contacts/detail/ContactDetailFragment.java b/src/com/android/contacts/detail/ContactDetailFragment.java
index 7701106..75cf1b3 100644
--- a/src/com/android/contacts/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailFragment.java
@@ -86,7 +86,7 @@
 import com.android.contacts.model.Contact;
 import com.android.contacts.model.RawContact;
 import com.android.contacts.model.RawContactDelta;
-import com.android.contacts.model.RawContactDelta.ValuesDelta;
+import com.android.contacts.model.ValuesDelta;
 import com.android.contacts.model.RawContactDeltaList;
 import com.android.contacts.model.RawContactModifier;
 import com.android.contacts.common.model.account.AccountType;
diff --git a/src/com/android/contacts/detail/PhotoSelectionHandler.java b/src/com/android/contacts/detail/PhotoSelectionHandler.java
index ec76379..d004566 100644
--- a/src/com/android/contacts/detail/PhotoSelectionHandler.java
+++ b/src/com/android/contacts/detail/PhotoSelectionHandler.java
@@ -42,7 +42,7 @@
 import com.android.contacts.common.model.AccountTypeManager;
 import com.android.contacts.model.RawContactModifier;
 import com.android.contacts.model.RawContactDelta;
-import com.android.contacts.model.RawContactDelta.ValuesDelta;
+import com.android.contacts.model.ValuesDelta;
 import com.android.contacts.common.model.account.AccountType;
 import com.android.contacts.model.RawContactDeltaList;
 import com.android.contacts.util.ContactPhotoUtils;
diff --git a/src/com/android/contacts/editor/AggregationSuggestionEngine.java b/src/com/android/contacts/editor/AggregationSuggestionEngine.java
index 0da05e8..4121d6a 100644
--- a/src/com/android/contacts/editor/AggregationSuggestionEngine.java
+++ b/src/com/android/contacts/editor/AggregationSuggestionEngine.java
@@ -37,7 +37,7 @@
 import android.provider.ContactsContract.RawContacts;
 import android.text.TextUtils;
 
-import com.android.contacts.model.RawContactDelta.ValuesDelta;
+import com.android.contacts.model.ValuesDelta;
 import com.google.common.collect.Lists;
 
 import java.util.ArrayList;
diff --git a/src/com/android/contacts/editor/BaseRawContactEditorView.java b/src/com/android/contacts/editor/BaseRawContactEditorView.java
index 1ff9ac2..48ef963 100644
--- a/src/com/android/contacts/editor/BaseRawContactEditorView.java
+++ b/src/com/android/contacts/editor/BaseRawContactEditorView.java
@@ -29,7 +29,7 @@
 
 import com.android.contacts.R;
 import com.android.contacts.model.RawContactDelta;
-import com.android.contacts.model.RawContactDelta.ValuesDelta;
+import com.android.contacts.model.ValuesDelta;
 import com.android.contacts.model.RawContactModifier;
 import com.android.contacts.common.model.account.AccountType;
 import com.android.contacts.common.model.account.AccountType.EditType;
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index 149f821..000a243 100644
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -75,7 +75,7 @@
 import com.android.contacts.model.ContactLoader;
 import com.android.contacts.model.RawContact;
 import com.android.contacts.model.RawContactDelta;
-import com.android.contacts.model.RawContactDelta.ValuesDelta;
+import com.android.contacts.model.ValuesDelta;
 import com.android.contacts.model.RawContactDeltaList;
 import com.android.contacts.model.RawContactModifier;
 import com.android.contacts.common.model.account.AccountType;
diff --git a/src/com/android/contacts/editor/Editor.java b/src/com/android/contacts/editor/Editor.java
index ec816f7..bafffe1 100644
--- a/src/com/android/contacts/editor/Editor.java
+++ b/src/com/android/contacts/editor/Editor.java
@@ -19,7 +19,7 @@
 import android.provider.ContactsContract.Data;
 
 import com.android.contacts.model.RawContactDelta;
-import com.android.contacts.model.RawContactDelta.ValuesDelta;
+import com.android.contacts.model.ValuesDelta;
 import com.android.contacts.common.model.dataitem.DataKind;
 
 /**
diff --git a/src/com/android/contacts/editor/EventFieldEditorView.java b/src/com/android/contacts/editor/EventFieldEditorView.java
index 1f6cd43..d2c4e4c 100644
--- a/src/com/android/contacts/editor/EventFieldEditorView.java
+++ b/src/com/android/contacts/editor/EventFieldEditorView.java
@@ -30,7 +30,7 @@
 import com.android.contacts.datepicker.DatePickerDialog;
 import com.android.contacts.datepicker.DatePickerDialog.OnDateSetListener;
 import com.android.contacts.model.RawContactDelta;
-import com.android.contacts.model.RawContactDelta.ValuesDelta;
+import com.android.contacts.model.ValuesDelta;
 import com.android.contacts.common.model.account.AccountType.EditField;
 import com.android.contacts.common.model.account.AccountType.EventEditType;
 import com.android.contacts.common.model.dataitem.DataKind;
diff --git a/src/com/android/contacts/editor/GroupMembershipView.java b/src/com/android/contacts/editor/GroupMembershipView.java
index 97257f1..f141011 100644
--- a/src/com/android/contacts/editor/GroupMembershipView.java
+++ b/src/com/android/contacts/editor/GroupMembershipView.java
@@ -41,7 +41,7 @@
 import com.android.contacts.interactions.GroupCreationDialogFragment;
 import com.android.contacts.interactions.GroupCreationDialogFragment.OnGroupCreatedListener;
 import com.android.contacts.model.RawContactDelta;
-import com.android.contacts.model.RawContactDelta.ValuesDelta;
+import com.android.contacts.model.ValuesDelta;
 import com.android.contacts.model.RawContactModifier;
 import com.google.common.base.Objects;
 
diff --git a/src/com/android/contacts/editor/KindSectionView.java b/src/com/android/contacts/editor/KindSectionView.java
index 4f70cd4..985775e 100644
--- a/src/com/android/contacts/editor/KindSectionView.java
+++ b/src/com/android/contacts/editor/KindSectionView.java
@@ -30,7 +30,7 @@
 import com.android.contacts.editor.Editor.EditorListener;
 import com.android.contacts.model.RawContactModifier;
 import com.android.contacts.model.RawContactDelta;
-import com.android.contacts.model.RawContactDelta.ValuesDelta;
+import com.android.contacts.model.ValuesDelta;
 import com.android.contacts.common.model.dataitem.DataKind;
 
 import java.util.ArrayList;
diff --git a/src/com/android/contacts/editor/LabeledEditorView.java b/src/com/android/contacts/editor/LabeledEditorView.java
index 91339ae..9b91d0d 100644
--- a/src/com/android/contacts/editor/LabeledEditorView.java
+++ b/src/com/android/contacts/editor/LabeledEditorView.java
@@ -47,7 +47,7 @@
 import com.android.contacts.ContactsUtils;
 import com.android.contacts.R;
 import com.android.contacts.model.RawContactDelta;
-import com.android.contacts.model.RawContactDelta.ValuesDelta;
+import com.android.contacts.model.ValuesDelta;
 import com.android.contacts.model.RawContactModifier;
 import com.android.contacts.common.model.account.AccountType.EditType;
 import com.android.contacts.common.model.dataitem.DataKind;
diff --git a/src/com/android/contacts/editor/PhoneticNameEditorView.java b/src/com/android/contacts/editor/PhoneticNameEditorView.java
index 1ee7e21..43050d9 100644
--- a/src/com/android/contacts/editor/PhoneticNameEditorView.java
+++ b/src/com/android/contacts/editor/PhoneticNameEditorView.java
@@ -22,7 +22,7 @@
 import android.util.AttributeSet;
 
 import com.android.contacts.model.RawContactDelta;
-import com.android.contacts.model.RawContactDelta.ValuesDelta;
+import com.android.contacts.model.ValuesDelta;
 import com.android.contacts.common.model.dataitem.DataKind;
 import com.android.contacts.model.dataitem.StructuredNameDataItem;
 
diff --git a/src/com/android/contacts/editor/PhotoEditorView.java b/src/com/android/contacts/editor/PhotoEditorView.java
index e106eac..9e20369 100644
--- a/src/com/android/contacts/editor/PhotoEditorView.java
+++ b/src/com/android/contacts/editor/PhotoEditorView.java
@@ -28,7 +28,7 @@
 import com.android.contacts.ContactsUtils;
 import com.android.contacts.R;
 import com.android.contacts.model.RawContactDelta;
-import com.android.contacts.model.RawContactDelta.ValuesDelta;
+import com.android.contacts.model.ValuesDelta;
 import com.android.contacts.common.model.dataitem.DataKind;
 import com.android.contacts.util.ContactPhotoUtils;
 
diff --git a/src/com/android/contacts/editor/RawContactEditorView.java b/src/com/android/contacts/editor/RawContactEditorView.java
index 27f2ce4..3d1cb85 100644
--- a/src/com/android/contacts/editor/RawContactEditorView.java
+++ b/src/com/android/contacts/editor/RawContactEditorView.java
@@ -42,7 +42,7 @@
 import com.android.contacts.common.model.account.AccountType.EditType;
 import com.android.contacts.common.model.dataitem.DataKind;
 import com.android.contacts.model.RawContactDelta;
-import com.android.contacts.model.RawContactDelta.ValuesDelta;
+import com.android.contacts.model.ValuesDelta;
 import com.android.contacts.model.RawContactModifier;
 import com.google.common.base.Objects;
 
diff --git a/src/com/android/contacts/editor/RawContactReadOnlyEditorView.java b/src/com/android/contacts/editor/RawContactReadOnlyEditorView.java
index 8f9dcb9..1dc0a39 100644
--- a/src/com/android/contacts/editor/RawContactReadOnlyEditorView.java
+++ b/src/com/android/contacts/editor/RawContactReadOnlyEditorView.java
@@ -41,7 +41,7 @@
 import com.android.contacts.common.GeoUtil;
 import com.android.contacts.model.RawContactModifier;
 import com.android.contacts.model.RawContactDelta;
-import com.android.contacts.model.RawContactDelta.ValuesDelta;
+import com.android.contacts.model.ValuesDelta;
 import com.android.contacts.common.model.account.AccountType;
 import com.android.contacts.common.model.account.AccountWithDataSet;
 import com.android.contacts.common.model.dataitem.DataKind;
diff --git a/src/com/android/contacts/editor/StructuredNameEditorView.java b/src/com/android/contacts/editor/StructuredNameEditorView.java
index fcafe75..e6b9e15 100644
--- a/src/com/android/contacts/editor/StructuredNameEditorView.java
+++ b/src/com/android/contacts/editor/StructuredNameEditorView.java
@@ -26,7 +26,7 @@
 import android.util.AttributeSet;
 
 import com.android.contacts.model.RawContactDelta;
-import com.android.contacts.model.RawContactDelta.ValuesDelta;
+import com.android.contacts.model.ValuesDelta;
 import com.android.contacts.model.dataitem.DataItem;
 import com.android.contacts.common.model.dataitem.DataKind;
 import com.android.contacts.model.dataitem.StructuredNameDataItem;
diff --git a/src/com/android/contacts/editor/TextFieldsEditorView.java b/src/com/android/contacts/editor/TextFieldsEditorView.java
index 970bbbf..fbaf680 100644
--- a/src/com/android/contacts/editor/TextFieldsEditorView.java
+++ b/src/com/android/contacts/editor/TextFieldsEditorView.java
@@ -38,7 +38,7 @@
 import com.android.contacts.ContactsUtils;
 import com.android.contacts.R;
 import com.android.contacts.model.RawContactDelta;
-import com.android.contacts.model.RawContactDelta.ValuesDelta;
+import com.android.contacts.model.ValuesDelta;
 import com.android.contacts.common.model.account.AccountType.EditField;
 import com.android.contacts.common.model.dataitem.DataKind;
 import com.android.contacts.common.util.PhoneNumberFormatter;
diff --git a/src/com/android/contacts/editor/ViewIdGenerator.java b/src/com/android/contacts/editor/ViewIdGenerator.java
index 9f41623..6e26177 100644
--- a/src/com/android/contacts/editor/ViewIdGenerator.java
+++ b/src/com/android/contacts/editor/ViewIdGenerator.java
@@ -21,7 +21,7 @@
 import android.os.Parcelable;
 
 import com.android.contacts.model.RawContactDelta;
-import com.android.contacts.model.RawContactDelta.ValuesDelta;
+import com.android.contacts.model.ValuesDelta;
 import com.android.contacts.common.model.dataitem.DataKind;
 
 /**
diff --git a/src/com/android/contacts/list/ContactListFilterView.java b/src/com/android/contacts/list/ContactListFilterView.java
index d905683..5fa8e4d 100644
--- a/src/com/android/contacts/list/ContactListFilterView.java
+++ b/src/com/android/contacts/list/ContactListFilterView.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2012 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -11,7 +11,7 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
  */
 
 package com.android.contacts.list;
diff --git a/src/com/android/contacts/list/CustomContactListFilterActivity.java b/src/com/android/contacts/list/CustomContactListFilterActivity.java
index 9750cf5..034f103 100644
--- a/src/com/android/contacts/list/CustomContactListFilterActivity.java
+++ b/src/com/android/contacts/list/CustomContactListFilterActivity.java
@@ -56,7 +56,7 @@
 import com.android.contacts.ContactsActivity;
 import com.android.contacts.R;
 import com.android.contacts.common.model.AccountTypeManager;
-import com.android.contacts.model.RawContactDelta.ValuesDelta;
+import com.android.contacts.model.ValuesDelta;
 import com.android.contacts.common.model.account.AccountType;
 import com.android.contacts.common.model.account.AccountWithDataSet;
 import com.android.contacts.common.model.account.GoogleAccountType;
diff --git a/src/com/android/contacts/model/RawContactDelta.java b/src/com/android/contacts/model/RawContactDelta.java
index 1a93cfb..34f039d 100644
--- a/src/com/android/contacts/model/RawContactDelta.java
+++ b/src/com/android/contacts/model/RawContactDelta.java
@@ -24,11 +24,6 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.provider.BaseColumns;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
-import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.Profile;
 import android.provider.ContactsContract.RawContacts;
@@ -39,13 +34,10 @@
 import com.android.contacts.common.test.NeededForTesting;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
 
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
+
 /**
  * Contains a {@link RawContact} and records any modifications separately so the
  * original {@link RawContact} can be swapped out with a newer version and the
@@ -560,544 +552,4 @@
         }
     };
 
-    /**
-     * Type of {@link ContentValues} that maintains both an original state and a
-     * modified version of that state. This allows us to build insert, update,
-     * or delete operations based on a "before" {@link Entity} snapshot.
-     */
-    public static class ValuesDelta implements Parcelable {
-        protected ContentValues mBefore;
-        protected ContentValues mAfter;
-        protected String mIdColumn = BaseColumns._ID;
-        private boolean mFromTemplate;
-
-        /**
-         * Next value to assign to {@link #mIdColumn} when building an insert
-         * operation through {@link #fromAfter(ContentValues)}. This is used so
-         * we can concretely reference this {@link ValuesDelta} before it has
-         * been persisted.
-         */
-        protected static int sNextInsertId = -1;
-
-        protected ValuesDelta() {
-        }
-
-        /**
-         * Create {@link ValuesDelta}, using the given object as the
-         * "before" state, usually from an {@link Entity}.
-         */
-        public static ValuesDelta fromBefore(ContentValues before) {
-            final ValuesDelta entry = new ValuesDelta();
-            entry.mBefore = before;
-            entry.mAfter = new ContentValues();
-            return entry;
-        }
-
-        /**
-         * Create {@link ValuesDelta}, using the given object as the "after"
-         * state, usually when we are inserting a row instead of updating.
-         */
-        public static ValuesDelta fromAfter(ContentValues after) {
-            final ValuesDelta entry = new ValuesDelta();
-            entry.mBefore = null;
-            entry.mAfter = after;
-
-            // Assign temporary id which is dropped before insert.
-            entry.mAfter.put(entry.mIdColumn, sNextInsertId--);
-            return entry;
-        }
-
-        @NeededForTesting
-        public ContentValues getAfter() {
-            return mAfter;
-        }
-
-        public boolean containsKey(String key) {
-            return ((mAfter != null && mAfter.containsKey(key)) ||
-                    (mBefore != null && mBefore.containsKey(key)));
-        }
-
-        public String getAsString(String key) {
-            if (mAfter != null && mAfter.containsKey(key)) {
-                return mAfter.getAsString(key);
-            } else if (mBefore != null && mBefore.containsKey(key)) {
-                return mBefore.getAsString(key);
-            } else {
-                return null;
-            }
-        }
-
-        public byte[] getAsByteArray(String key) {
-            if (mAfter != null && mAfter.containsKey(key)) {
-                return mAfter.getAsByteArray(key);
-            } else if (mBefore != null && mBefore.containsKey(key)) {
-                return mBefore.getAsByteArray(key);
-            } else {
-                return null;
-            }
-        }
-
-        public Long getAsLong(String key) {
-            if (mAfter != null && mAfter.containsKey(key)) {
-                return mAfter.getAsLong(key);
-            } else if (mBefore != null && mBefore.containsKey(key)) {
-                return mBefore.getAsLong(key);
-            } else {
-                return null;
-            }
-        }
-
-        public Integer getAsInteger(String key) {
-            return getAsInteger(key, null);
-        }
-
-        public Integer getAsInteger(String key, Integer defaultValue) {
-            if (mAfter != null && mAfter.containsKey(key)) {
-                return mAfter.getAsInteger(key);
-            } else if (mBefore != null && mBefore.containsKey(key)) {
-                return mBefore.getAsInteger(key);
-            } else {
-                return defaultValue;
-            }
-        }
-
-        public boolean isChanged(String key) {
-            if (mAfter == null || !mAfter.containsKey(key)) {
-                return false;
-            }
-
-            Object newValue = mAfter.get(key);
-            Object oldValue = mBefore.get(key);
-
-            if (oldValue == null) {
-                return newValue != null;
-            }
-
-            return !oldValue.equals(newValue);
-        }
-
-        public String getMimetype() {
-            return getAsString(Data.MIMETYPE);
-        }
-
-        public Long getId() {
-            return getAsLong(mIdColumn);
-        }
-
-        public void setIdColumn(String idColumn) {
-            mIdColumn = idColumn;
-        }
-
-        public boolean isPrimary() {
-            final Long isPrimary = getAsLong(Data.IS_PRIMARY);
-            return isPrimary == null ? false : isPrimary != 0;
-        }
-
-        public void setFromTemplate(boolean isFromTemplate) {
-            mFromTemplate = isFromTemplate;
-        }
-
-        public boolean isFromTemplate() {
-            return mFromTemplate;
-        }
-
-        public boolean isSuperPrimary() {
-            final Long isSuperPrimary = getAsLong(Data.IS_SUPER_PRIMARY);
-            return isSuperPrimary == null ? false : isSuperPrimary != 0;
-        }
-
-        public boolean beforeExists() {
-            return (mBefore != null && mBefore.containsKey(mIdColumn));
-        }
-
-        /**
-         * When "after" is present, then visible
-         */
-        public boolean isVisible() {
-            return (mAfter != null);
-        }
-
-        /**
-         * When "after" is wiped, action is "delete"
-         */
-        public boolean isDelete() {
-            return beforeExists() && (mAfter == null);
-        }
-
-        /**
-         * When no "before" or "after", is transient
-         */
-        public boolean isTransient() {
-            return (mBefore == null) && (mAfter == null);
-        }
-
-        /**
-         * When "after" has some changes, action is "update"
-         */
-        public boolean isUpdate() {
-            if (!beforeExists() || mAfter == null || mAfter.size() == 0) {
-                return false;
-            }
-            for (String key : mAfter.keySet()) {
-                Object newValue = mAfter.get(key);
-                Object oldValue = mBefore.get(key);
-                if (oldValue == null) {
-                    if (newValue != null) {
-                        return true;
-                    }
-                } else if (!oldValue.equals(newValue)) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        /**
-         * When "after" has no changes, action is no-op
-         */
-        public boolean isNoop() {
-            return beforeExists() && (mAfter != null && mAfter.size() == 0);
-        }
-
-        /**
-         * When no "before" id, and has "after", action is "insert"
-         */
-        public boolean isInsert() {
-            return !beforeExists() && (mAfter != null);
-        }
-
-        public void markDeleted() {
-            mAfter = null;
-        }
-
-        /**
-         * Ensure that our internal structure is ready for storing updates.
-         */
-        private void ensureUpdate() {
-            if (mAfter == null) {
-                mAfter = new ContentValues();
-            }
-        }
-
-        public void put(String key, String value) {
-            ensureUpdate();
-            mAfter.put(key, value);
-        }
-
-        public void put(String key, byte[] value) {
-            ensureUpdate();
-            mAfter.put(key, value);
-        }
-
-        public void put(String key, int value) {
-            ensureUpdate();
-            mAfter.put(key, value);
-        }
-
-        public void put(String key, long value) {
-            ensureUpdate();
-            mAfter.put(key, value);
-        }
-
-        public void putNull(String key) {
-            ensureUpdate();
-            mAfter.putNull(key);
-        }
-
-        public void copyStringFrom(ValuesDelta from, String key) {
-            ensureUpdate();
-            put(key, from.getAsString(key));
-        }
-
-        /**
-         * Return set of all keys defined through this object.
-         */
-        public Set<String> keySet() {
-            final HashSet<String> keys = Sets.newHashSet();
-
-            if (mBefore != null) {
-                for (Map.Entry<String, Object> entry : mBefore.valueSet()) {
-                    keys.add(entry.getKey());
-                }
-            }
-
-            if (mAfter != null) {
-                for (Map.Entry<String, Object> entry : mAfter.valueSet()) {
-                    keys.add(entry.getKey());
-                }
-            }
-
-            return keys;
-        }
-
-        /**
-         * Return complete set of "before" and "after" values mixed together,
-         * giving full state regardless of edits.
-         */
-        public ContentValues getCompleteValues() {
-            final ContentValues values = new ContentValues();
-            if (mBefore != null) {
-                values.putAll(mBefore);
-            }
-            if (mAfter != null) {
-                values.putAll(mAfter);
-            }
-            if (values.containsKey(GroupMembership.GROUP_ROW_ID)) {
-                // Clear to avoid double-definitions, and prefer rows
-                values.remove(GroupMembership.GROUP_SOURCE_ID);
-            }
-
-            return values;
-        }
-
-        /**
-         * Merge the "after" values from the given {@link ValuesDelta},
-         * discarding any existing "after" state. This is typically used when
-         * re-parenting changes onto an updated {@link Entity}.
-         */
-        public static ValuesDelta mergeAfter(ValuesDelta local, ValuesDelta remote) {
-            // Bail early if trying to merge delete with missing local
-            if (local == null && (remote.isDelete() || remote.isTransient())) return null;
-
-            // Create local version if none exists yet
-            if (local == null) local = new ValuesDelta();
-
-            if (!local.beforeExists()) {
-                // Any "before" record is missing, so take all values as "insert"
-                local.mAfter = remote.getCompleteValues();
-            } else {
-                // Existing "update" with only "after" values
-                local.mAfter = remote.mAfter;
-            }
-
-            return local;
-        }
-
-        @Override
-        public boolean equals(Object object) {
-            if (object instanceof ValuesDelta) {
-                // Only exactly equal with both are identical subsets
-                final ValuesDelta other = (ValuesDelta)object;
-                return this.subsetEquals(other) && other.subsetEquals(this);
-            }
-            return false;
-        }
-
-        @Override
-        public String toString() {
-            final StringBuilder builder = new StringBuilder();
-            toString(builder);
-            return builder.toString();
-        }
-
-        /**
-         * Helper for building string representation, leveraging the given
-         * {@link StringBuilder} to minimize allocations.
-         */
-        public void toString(StringBuilder builder) {
-            builder.append("{ ");
-            builder.append("IdColumn=");
-            builder.append(mIdColumn);
-            builder.append(", FromTemplate=");
-            builder.append(mFromTemplate);
-            builder.append(", ");
-            for (String key : this.keySet()) {
-                builder.append(key);
-                builder.append("=");
-                builder.append(this.getAsString(key));
-                builder.append(", ");
-            }
-            builder.append("}");
-        }
-
-        /**
-         * Check if the given {@link ValuesDelta} is both a subset of this
-         * object, and any defined keys have equal values.
-         */
-        public boolean subsetEquals(ValuesDelta other) {
-            for (String key : this.keySet()) {
-                final String ourValue = this.getAsString(key);
-                final String theirValue = other.getAsString(key);
-                if (ourValue == null) {
-                    // If they have value when we're null, no match
-                    if (theirValue != null) return false;
-                } else {
-                    // If both values defined and aren't equal, no match
-                    if (!ourValue.equals(theirValue)) return false;
-                }
-            }
-            // All values compared and matched
-            return true;
-        }
-
-        /**
-         * Build a {@link ContentProviderOperation} that will transform our
-         * "before" state into our "after" state, using insert, update, or
-         * delete as needed.
-         */
-        public ContentProviderOperation.Builder buildDiff(Uri targetUri) {
-            Builder builder = null;
-            if (isInsert()) {
-                // Changed values are "insert" back-referenced to Contact
-                mAfter.remove(mIdColumn);
-                builder = ContentProviderOperation.newInsert(targetUri);
-                builder.withValues(mAfter);
-            } else if (isDelete()) {
-                // When marked for deletion and "before" exists, then "delete"
-                builder = ContentProviderOperation.newDelete(targetUri);
-                builder.withSelection(mIdColumn + "=" + getId(), null);
-            } else if (isUpdate()) {
-                // When has changes and "before" exists, then "update"
-                builder = ContentProviderOperation.newUpdate(targetUri);
-                builder.withSelection(mIdColumn + "=" + getId(), null);
-                builder.withValues(mAfter);
-            }
-            return builder;
-        }
-
-        /** {@inheritDoc} */
-        public int describeContents() {
-            // Nothing special about this parcel
-            return 0;
-        }
-
-        /** {@inheritDoc} */
-        public void writeToParcel(Parcel dest, int flags) {
-            dest.writeParcelable(mBefore, flags);
-            dest.writeParcelable(mAfter, flags);
-            dest.writeString(mIdColumn);
-        }
-
-        public void readFromParcel(Parcel source) {
-            final ClassLoader loader = getClass().getClassLoader();
-            mBefore = source.<ContentValues> readParcelable(loader);
-            mAfter = source.<ContentValues> readParcelable(loader);
-            mIdColumn = source.readString();
-        }
-
-        public static final Parcelable.Creator<ValuesDelta> CREATOR = new Parcelable.Creator<ValuesDelta>() {
-            public ValuesDelta createFromParcel(Parcel in) {
-                final ValuesDelta values = new ValuesDelta();
-                values.readFromParcel(in);
-                return values;
-            }
-
-            public ValuesDelta[] newArray(int size) {
-                return new ValuesDelta[size];
-            }
-        };
-
-        public void setGroupRowId(long groupId) {
-            put(GroupMembership.GROUP_ROW_ID, groupId);
-        }
-
-        public Long getGroupRowId() {
-            return getAsLong(GroupMembership.GROUP_ROW_ID);
-        }
-
-        public void setPhoto(byte[] value) {
-            put(Photo.PHOTO, value);
-        }
-
-        public byte[] getPhoto() {
-            return getAsByteArray(Photo.PHOTO);
-        }
-
-        public void setSuperPrimary(boolean val) {
-            if (val) {
-                put(Data.IS_SUPER_PRIMARY, 1);
-            } else {
-                put(Data.IS_SUPER_PRIMARY, 0);
-            }
-        }
-
-        public void setPhoneticFamilyName(String value) {
-            put(StructuredName.PHONETIC_FAMILY_NAME, value);
-        }
-
-        public void setPhoneticMiddleName(String value) {
-            put(StructuredName.PHONETIC_MIDDLE_NAME, value);
-        }
-
-        public void setPhoneticGivenName(String value) {
-            put(StructuredName.PHONETIC_GIVEN_NAME, value);
-        }
-
-        public String getPhoneticFamilyName() {
-            return getAsString(StructuredName.PHONETIC_FAMILY_NAME);
-        }
-
-        public String getPhoneticMiddleName() {
-            return getAsString(StructuredName.PHONETIC_MIDDLE_NAME);
-        }
-
-        public String getPhoneticGivenName() {
-            return getAsString(StructuredName.PHONETIC_GIVEN_NAME);
-        }
-
-        public String getDisplayName() {
-            return getAsString(StructuredName.DISPLAY_NAME);
-        }
-
-        public void setDisplayName(String name) {
-            if (name == null) {
-                putNull(StructuredName.DISPLAY_NAME);
-            } else {
-                put(StructuredName.DISPLAY_NAME, name);
-            }
-        }
-
-        public void copyStructuredNameFieldsFrom(ValuesDelta name) {
-            copyStringFrom(name, StructuredName.DISPLAY_NAME);
-
-            copyStringFrom(name, StructuredName.GIVEN_NAME);
-            copyStringFrom(name, StructuredName.FAMILY_NAME);
-            copyStringFrom(name, StructuredName.PREFIX);
-            copyStringFrom(name, StructuredName.MIDDLE_NAME);
-            copyStringFrom(name, StructuredName.SUFFIX);
-
-            copyStringFrom(name, StructuredName.PHONETIC_GIVEN_NAME);
-            copyStringFrom(name, StructuredName.PHONETIC_MIDDLE_NAME);
-            copyStringFrom(name, StructuredName.PHONETIC_FAMILY_NAME);
-
-            copyStringFrom(name, StructuredName.FULL_NAME_STYLE);
-            copyStringFrom(name, StructuredName.PHONETIC_NAME_STYLE);
-        }
-
-        public String getPhoneNumber() {
-            return getAsString(Phone.NUMBER);
-        }
-
-        public String getPhoneNormalizedNumber() {
-            return getAsString(Phone.NORMALIZED_NUMBER);
-        }
-
-        public boolean phoneHasType() {
-            return containsKey(Phone.TYPE);
-        }
-
-        public int getPhoneType() {
-            return getAsInteger(Phone.TYPE);
-        }
-
-        public String getPhoneLabel() {
-            return getAsString(Phone.LABEL);
-        }
-
-        public String getEmailData() {
-            return getAsString(Email.DATA);
-        }
-
-        public boolean emailHasType() {
-            return containsKey(Email.TYPE);
-        }
-
-        public int getEmailType() {
-            return getAsInteger(Email.TYPE);
-        }
-
-        public String getEmailLabel() {
-            return getAsString(Email.LABEL);
-        }
-    }
 }
diff --git a/src/com/android/contacts/model/RawContactDeltaList.java b/src/com/android/contacts/model/RawContactDeltaList.java
index 82dd494..3169c5d 100644
--- a/src/com/android/contacts/model/RawContactDeltaList.java
+++ b/src/com/android/contacts/model/RawContactDeltaList.java
@@ -30,7 +30,6 @@
 import android.provider.ContactsContract.RawContacts;
 import android.util.Log;
 
-import com.android.contacts.model.RawContactDelta.ValuesDelta;
 import com.google.common.collect.Lists;
 
 import java.util.ArrayList;
diff --git a/src/com/android/contacts/model/RawContactModifier.java b/src/com/android/contacts/model/RawContactModifier.java
index 27162a1..d640f6b 100644
--- a/src/com/android/contacts/model/RawContactModifier.java
+++ b/src/com/android/contacts/model/RawContactModifier.java
@@ -51,7 +51,6 @@
 import com.android.contacts.common.util.CommonDateUtils;
 import com.android.contacts.editor.EventFieldEditorView;
 import com.android.contacts.editor.PhoneticNameEditorView;
-import com.android.contacts.model.RawContactDelta.ValuesDelta;
 import com.android.contacts.common.model.account.AccountType;
 import com.android.contacts.common.model.account.AccountType.EditField;
 import com.android.contacts.common.model.account.AccountType.EditType;
diff --git a/src/com/android/contacts/model/ValuesDelta.java b/src/com/android/contacts/model/ValuesDelta.java
new file mode 100644
index 0000000..d9ade77
--- /dev/null
+++ b/src/com/android/contacts/model/ValuesDelta.java
@@ -0,0 +1,573 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.model;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentValues;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.provider.BaseColumns;
+import android.provider.ContactsContract;
+
+import com.android.contacts.common.test.NeededForTesting;
+import com.google.common.collect.Sets;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Type of {@link android.content.ContentValues} that maintains both an original state and a
+ * modified version of that state. This allows us to build insert, update,
+ * or delete operations based on a "before" {@link Entity} snapshot.
+ */
+public class ValuesDelta implements Parcelable {
+    protected ContentValues mBefore;
+    protected ContentValues mAfter;
+    protected String mIdColumn = BaseColumns._ID;
+    private boolean mFromTemplate;
+
+    /**
+     * Next value to assign to {@link #mIdColumn} when building an insert
+     * operation through {@link #fromAfter(android.content.ContentValues)}. This is used so
+     * we can concretely reference this {@link ValuesDelta} before it has
+     * been persisted.
+     */
+    protected static int sNextInsertId = -1;
+
+    protected ValuesDelta() {
+    }
+
+    /**
+     * Create {@link ValuesDelta}, using the given object as the
+     * "before" state, usually from an {@link Entity}.
+     */
+    public static ValuesDelta fromBefore(ContentValues before) {
+        final ValuesDelta entry = new ValuesDelta();
+        entry.mBefore = before;
+        entry.mAfter = new ContentValues();
+        return entry;
+    }
+
+    /**
+     * Create {@link ValuesDelta}, using the given object as the "after"
+     * state, usually when we are inserting a row instead of updating.
+     */
+    public static ValuesDelta fromAfter(ContentValues after) {
+        final ValuesDelta entry = new ValuesDelta();
+        entry.mBefore = null;
+        entry.mAfter = after;
+
+        // Assign temporary id which is dropped before insert.
+        entry.mAfter.put(entry.mIdColumn, sNextInsertId--);
+        return entry;
+    }
+
+    @NeededForTesting
+    public ContentValues getAfter() {
+        return mAfter;
+    }
+
+    public boolean containsKey(String key) {
+        return ((mAfter != null && mAfter.containsKey(key)) ||
+                (mBefore != null && mBefore.containsKey(key)));
+    }
+
+    public String getAsString(String key) {
+        if (mAfter != null && mAfter.containsKey(key)) {
+            return mAfter.getAsString(key);
+        } else if (mBefore != null && mBefore.containsKey(key)) {
+            return mBefore.getAsString(key);
+        } else {
+            return null;
+        }
+    }
+
+    public byte[] getAsByteArray(String key) {
+        if (mAfter != null && mAfter.containsKey(key)) {
+            return mAfter.getAsByteArray(key);
+        } else if (mBefore != null && mBefore.containsKey(key)) {
+            return mBefore.getAsByteArray(key);
+        } else {
+            return null;
+        }
+    }
+
+    public Long getAsLong(String key) {
+        if (mAfter != null && mAfter.containsKey(key)) {
+            return mAfter.getAsLong(key);
+        } else if (mBefore != null && mBefore.containsKey(key)) {
+            return mBefore.getAsLong(key);
+        } else {
+            return null;
+        }
+    }
+
+    public Integer getAsInteger(String key) {
+        return getAsInteger(key, null);
+    }
+
+    public Integer getAsInteger(String key, Integer defaultValue) {
+        if (mAfter != null && mAfter.containsKey(key)) {
+            return mAfter.getAsInteger(key);
+        } else if (mBefore != null && mBefore.containsKey(key)) {
+            return mBefore.getAsInteger(key);
+        } else {
+            return defaultValue;
+        }
+    }
+
+    public boolean isChanged(String key) {
+        if (mAfter == null || !mAfter.containsKey(key)) {
+            return false;
+        }
+
+        Object newValue = mAfter.get(key);
+        Object oldValue = mBefore.get(key);
+
+        if (oldValue == null) {
+            return newValue != null;
+        }
+
+        return !oldValue.equals(newValue);
+    }
+
+    public String getMimetype() {
+        return getAsString(ContactsContract.Data.MIMETYPE);
+    }
+
+    public Long getId() {
+        return getAsLong(mIdColumn);
+    }
+
+    public void setIdColumn(String idColumn) {
+        mIdColumn = idColumn;
+    }
+
+    public boolean isPrimary() {
+        final Long isPrimary = getAsLong(ContactsContract.Data.IS_PRIMARY);
+        return isPrimary == null ? false : isPrimary != 0;
+    }
+
+    public void setFromTemplate(boolean isFromTemplate) {
+        mFromTemplate = isFromTemplate;
+    }
+
+    public boolean isFromTemplate() {
+        return mFromTemplate;
+    }
+
+    public boolean isSuperPrimary() {
+        final Long isSuperPrimary = getAsLong(ContactsContract.Data.IS_SUPER_PRIMARY);
+        return isSuperPrimary == null ? false : isSuperPrimary != 0;
+    }
+
+    public boolean beforeExists() {
+        return (mBefore != null && mBefore.containsKey(mIdColumn));
+    }
+
+    /**
+     * When "after" is present, then visible
+     */
+    public boolean isVisible() {
+        return (mAfter != null);
+    }
+
+    /**
+     * When "after" is wiped, action is "delete"
+     */
+    public boolean isDelete() {
+        return beforeExists() && (mAfter == null);
+    }
+
+    /**
+     * When no "before" or "after", is transient
+     */
+    public boolean isTransient() {
+        return (mBefore == null) && (mAfter == null);
+    }
+
+    /**
+     * When "after" has some changes, action is "update"
+     */
+    public boolean isUpdate() {
+        if (!beforeExists() || mAfter == null || mAfter.size() == 0) {
+            return false;
+        }
+        for (String key : mAfter.keySet()) {
+            Object newValue = mAfter.get(key);
+            Object oldValue = mBefore.get(key);
+            if (oldValue == null) {
+                if (newValue != null) {
+                    return true;
+                }
+            } else if (!oldValue.equals(newValue)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * When "after" has no changes, action is no-op
+     */
+    public boolean isNoop() {
+        return beforeExists() && (mAfter != null && mAfter.size() == 0);
+    }
+
+    /**
+     * When no "before" id, and has "after", action is "insert"
+     */
+    public boolean isInsert() {
+        return !beforeExists() && (mAfter != null);
+    }
+
+    public void markDeleted() {
+        mAfter = null;
+    }
+
+    /**
+     * Ensure that our internal structure is ready for storing updates.
+     */
+    private void ensureUpdate() {
+        if (mAfter == null) {
+            mAfter = new ContentValues();
+        }
+    }
+
+    public void put(String key, String value) {
+        ensureUpdate();
+        mAfter.put(key, value);
+    }
+
+    public void put(String key, byte[] value) {
+        ensureUpdate();
+        mAfter.put(key, value);
+    }
+
+    public void put(String key, int value) {
+        ensureUpdate();
+        mAfter.put(key, value);
+    }
+
+    public void put(String key, long value) {
+        ensureUpdate();
+        mAfter.put(key, value);
+    }
+
+    public void putNull(String key) {
+        ensureUpdate();
+        mAfter.putNull(key);
+    }
+
+    public void copyStringFrom(ValuesDelta from, String key) {
+        ensureUpdate();
+        put(key, from.getAsString(key));
+    }
+
+    /**
+     * Return set of all keys defined through this object.
+     */
+    public Set<String> keySet() {
+        final HashSet<String> keys = Sets.newHashSet();
+
+        if (mBefore != null) {
+            for (Map.Entry<String, Object> entry : mBefore.valueSet()) {
+                keys.add(entry.getKey());
+            }
+        }
+
+        if (mAfter != null) {
+            for (Map.Entry<String, Object> entry : mAfter.valueSet()) {
+                keys.add(entry.getKey());
+            }
+        }
+
+        return keys;
+    }
+
+    /**
+     * Return complete set of "before" and "after" values mixed together,
+     * giving full state regardless of edits.
+     */
+    public ContentValues getCompleteValues() {
+        final ContentValues values = new ContentValues();
+        if (mBefore != null) {
+            values.putAll(mBefore);
+        }
+        if (mAfter != null) {
+            values.putAll(mAfter);
+        }
+        if (values.containsKey(ContactsContract.CommonDataKinds.GroupMembership.GROUP_ROW_ID)) {
+            // Clear to avoid double-definitions, and prefer rows
+            values.remove(ContactsContract.CommonDataKinds.GroupMembership.GROUP_SOURCE_ID);
+        }
+
+        return values;
+    }
+
+    /**
+     * Merge the "after" values from the given {@link ValuesDelta},
+     * discarding any existing "after" state. This is typically used when
+     * re-parenting changes onto an updated {@link Entity}.
+     */
+    public static ValuesDelta mergeAfter(ValuesDelta local, ValuesDelta remote) {
+        // Bail early if trying to merge delete with missing local
+        if (local == null && (remote.isDelete() || remote.isTransient())) return null;
+
+        // Create local version if none exists yet
+        if (local == null) local = new ValuesDelta();
+
+        if (!local.beforeExists()) {
+            // Any "before" record is missing, so take all values as "insert"
+            local.mAfter = remote.getCompleteValues();
+        } else {
+            // Existing "update" with only "after" values
+            local.mAfter = remote.mAfter;
+        }
+
+        return local;
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (object instanceof ValuesDelta) {
+            // Only exactly equal with both are identical subsets
+            final ValuesDelta other = (ValuesDelta)object;
+            return this.subsetEquals(other) && other.subsetEquals(this);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder builder = new StringBuilder();
+        toString(builder);
+        return builder.toString();
+    }
+
+    /**
+     * Helper for building string representation, leveraging the given
+     * {@link StringBuilder} to minimize allocations.
+     */
+    public void toString(StringBuilder builder) {
+        builder.append("{ ");
+        builder.append("IdColumn=");
+        builder.append(mIdColumn);
+        builder.append(", FromTemplate=");
+        builder.append(mFromTemplate);
+        builder.append(", ");
+        for (String key : this.keySet()) {
+            builder.append(key);
+            builder.append("=");
+            builder.append(this.getAsString(key));
+            builder.append(", ");
+        }
+        builder.append("}");
+    }
+
+    /**
+     * Check if the given {@link ValuesDelta} is both a subset of this
+     * object, and any defined keys have equal values.
+     */
+    public boolean subsetEquals(ValuesDelta other) {
+        for (String key : this.keySet()) {
+            final String ourValue = this.getAsString(key);
+            final String theirValue = other.getAsString(key);
+            if (ourValue == null) {
+                // If they have value when we're null, no match
+                if (theirValue != null) return false;
+            } else {
+                // If both values defined and aren't equal, no match
+                if (!ourValue.equals(theirValue)) return false;
+            }
+        }
+        // All values compared and matched
+        return true;
+    }
+
+    /**
+     * Build a {@link android.content.ContentProviderOperation} that will transform our
+     * "before" state into our "after" state, using insert, update, or
+     * delete as needed.
+     */
+    public ContentProviderOperation.Builder buildDiff(Uri targetUri) {
+        ContentProviderOperation.Builder builder = null;
+        if (isInsert()) {
+            // Changed values are "insert" back-referenced to Contact
+            mAfter.remove(mIdColumn);
+            builder = ContentProviderOperation.newInsert(targetUri);
+            builder.withValues(mAfter);
+        } else if (isDelete()) {
+            // When marked for deletion and "before" exists, then "delete"
+            builder = ContentProviderOperation.newDelete(targetUri);
+            builder.withSelection(mIdColumn + "=" + getId(), null);
+        } else if (isUpdate()) {
+            // When has changes and "before" exists, then "update"
+            builder = ContentProviderOperation.newUpdate(targetUri);
+            builder.withSelection(mIdColumn + "=" + getId(), null);
+            builder.withValues(mAfter);
+        }
+        return builder;
+    }
+
+    /** {@inheritDoc} */
+    public int describeContents() {
+        // Nothing special about this parcel
+        return 0;
+    }
+
+    /** {@inheritDoc} */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(mBefore, flags);
+        dest.writeParcelable(mAfter, flags);
+        dest.writeString(mIdColumn);
+    }
+
+    public void readFromParcel(Parcel source) {
+        final ClassLoader loader = getClass().getClassLoader();
+        mBefore = source.<ContentValues> readParcelable(loader);
+        mAfter = source.<ContentValues> readParcelable(loader);
+        mIdColumn = source.readString();
+    }
+
+    public static final Creator<ValuesDelta> CREATOR = new Creator<ValuesDelta>() {
+        public ValuesDelta createFromParcel(Parcel in) {
+            final ValuesDelta values = new ValuesDelta();
+            values.readFromParcel(in);
+            return values;
+        }
+
+        public ValuesDelta[] newArray(int size) {
+            return new ValuesDelta[size];
+        }
+    };
+
+    public void setGroupRowId(long groupId) {
+        put(ContactsContract.CommonDataKinds.GroupMembership.GROUP_ROW_ID, groupId);
+    }
+
+    public Long getGroupRowId() {
+        return getAsLong(ContactsContract.CommonDataKinds.GroupMembership.GROUP_ROW_ID);
+    }
+
+    public void setPhoto(byte[] value) {
+        put(ContactsContract.CommonDataKinds.Photo.PHOTO, value);
+    }
+
+    public byte[] getPhoto() {
+        return getAsByteArray(ContactsContract.CommonDataKinds.Photo.PHOTO);
+    }
+
+    public void setSuperPrimary(boolean val) {
+        if (val) {
+            put(ContactsContract.Data.IS_SUPER_PRIMARY, 1);
+        } else {
+            put(ContactsContract.Data.IS_SUPER_PRIMARY, 0);
+        }
+    }
+
+    public void setPhoneticFamilyName(String value) {
+        put(ContactsContract.CommonDataKinds.StructuredName.PHONETIC_FAMILY_NAME, value);
+    }
+
+    public void setPhoneticMiddleName(String value) {
+        put(ContactsContract.CommonDataKinds.StructuredName.PHONETIC_MIDDLE_NAME, value);
+    }
+
+    public void setPhoneticGivenName(String value) {
+        put(ContactsContract.CommonDataKinds.StructuredName.PHONETIC_GIVEN_NAME, value);
+    }
+
+    public String getPhoneticFamilyName() {
+        return getAsString(ContactsContract.CommonDataKinds.StructuredName.PHONETIC_FAMILY_NAME);
+    }
+
+    public String getPhoneticMiddleName() {
+        return getAsString(ContactsContract.CommonDataKinds.StructuredName.PHONETIC_MIDDLE_NAME);
+    }
+
+    public String getPhoneticGivenName() {
+        return getAsString(ContactsContract.CommonDataKinds.StructuredName.PHONETIC_GIVEN_NAME);
+    }
+
+    public String getDisplayName() {
+        return getAsString(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME);
+    }
+
+    public void setDisplayName(String name) {
+        if (name == null) {
+            putNull(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME);
+        } else {
+            put(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name);
+        }
+    }
+
+    public void copyStructuredNameFieldsFrom(ValuesDelta name) {
+        copyStringFrom(name, ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME);
+
+        copyStringFrom(name, ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME);
+        copyStringFrom(name, ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME);
+        copyStringFrom(name, ContactsContract.CommonDataKinds.StructuredName.PREFIX);
+        copyStringFrom(name, ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME);
+        copyStringFrom(name, ContactsContract.CommonDataKinds.StructuredName.SUFFIX);
+
+        copyStringFrom(name, ContactsContract.CommonDataKinds.StructuredName.PHONETIC_GIVEN_NAME);
+        copyStringFrom(name, ContactsContract.CommonDataKinds.StructuredName.PHONETIC_MIDDLE_NAME);
+        copyStringFrom(name, ContactsContract.CommonDataKinds.StructuredName.PHONETIC_FAMILY_NAME);
+
+        copyStringFrom(name, ContactsContract.CommonDataKinds.StructuredName.FULL_NAME_STYLE);
+        copyStringFrom(name, ContactsContract.CommonDataKinds.StructuredName.PHONETIC_NAME_STYLE);
+    }
+
+    public String getPhoneNumber() {
+        return getAsString(ContactsContract.CommonDataKinds.Phone.NUMBER);
+    }
+
+    public String getPhoneNormalizedNumber() {
+        return getAsString(ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER);
+    }
+
+    public boolean phoneHasType() {
+        return containsKey(ContactsContract.CommonDataKinds.Phone.TYPE);
+    }
+
+    public int getPhoneType() {
+        return getAsInteger(ContactsContract.CommonDataKinds.Phone.TYPE);
+    }
+
+    public String getPhoneLabel() {
+        return getAsString(ContactsContract.CommonDataKinds.Phone.LABEL);
+    }
+
+    public String getEmailData() {
+        return getAsString(ContactsContract.CommonDataKinds.Email.DATA);
+    }
+
+    public boolean emailHasType() {
+        return containsKey(ContactsContract.CommonDataKinds.Email.TYPE);
+    }
+
+    public int getEmailType() {
+        return getAsInteger(ContactsContract.CommonDataKinds.Email.TYPE);
+    }
+
+    public String getEmailLabel() {
+        return getAsString(ContactsContract.CommonDataKinds.Email.LABEL);
+    }
+}
diff --git a/tests/src/com/android/contacts/RawContactDeltaListTests.java b/tests/src/com/android/contacts/RawContactDeltaListTests.java
index ef7667b..a8a5d7b 100644
--- a/tests/src/com/android/contacts/RawContactDeltaListTests.java
+++ b/tests/src/com/android/contacts/RawContactDeltaListTests.java
@@ -37,7 +37,7 @@
 import com.android.contacts.RawContactModifierTests.MockContactsSource;
 import com.android.contacts.model.RawContact;
 import com.android.contacts.model.RawContactDelta;
-import com.android.contacts.model.RawContactDelta.ValuesDelta;
+import com.android.contacts.model.ValuesDelta;
 import com.android.contacts.model.RawContactDeltaList;
 import com.android.contacts.model.RawContactModifier;
 import com.android.contacts.common.model.account.AccountType;
diff --git a/tests/src/com/android/contacts/RawContactDeltaTests.java b/tests/src/com/android/contacts/RawContactDeltaTests.java
index 751d41c..62b7975 100644
--- a/tests/src/com/android/contacts/RawContactDeltaTests.java
+++ b/tests/src/com/android/contacts/RawContactDeltaTests.java
@@ -34,7 +34,7 @@
 
 import com.android.contacts.model.RawContact;
 import com.android.contacts.model.RawContactDelta;
-import com.android.contacts.model.RawContactDelta.ValuesDelta;
+import com.android.contacts.model.ValuesDelta;
 import com.google.common.collect.Lists;
 
 import java.util.ArrayList;
@@ -143,49 +143,6 @@
         assertEquals("Unexpected change when merging", source, merged);
     }
 
-    /**
-     * Test that {@link ValuesDelta#buildDiff(android.net.Uri)} is correctly
-     * built for insert, update, and delete cases. Note this only tests behavior
-     * for individual {@link Data} rows.
-     */
-    public void testValuesDiffNone() {
-        final ContentValues before = new ContentValues();
-        before.put(Data._ID, TEST_PHONE_ID);
-        before.put(Phone.NUMBER, TEST_PHONE_NUMBER_1);
-
-        final ValuesDelta values = ValuesDelta.fromBefore(before);
-
-        // None action shouldn't produce a builder
-        final Builder builder = values.buildDiff(Data.CONTENT_URI);
-        assertNull("None action produced a builder", builder);
-    }
-
-    public void testValuesDiffInsert() {
-        final ContentValues after = new ContentValues();
-        after.put(Phone.NUMBER, TEST_PHONE_NUMBER_2);
-
-        final ValuesDelta values = ValuesDelta.fromAfter(after);
-
-        // Should produce an insert action
-        final Builder builder = values.buildDiff(Data.CONTENT_URI);
-        final int type = builder.build().getType();
-        assertEquals("Didn't produce insert action", TYPE_INSERT, type);
-    }
-
-    public void testValuesDiffUpdate() {
-        final ContentValues before = new ContentValues();
-        before.put(Data._ID, TEST_PHONE_ID);
-        before.put(Phone.NUMBER, TEST_PHONE_NUMBER_1);
-
-        final ValuesDelta values = ValuesDelta.fromBefore(before);
-        values.put(Phone.NUMBER, TEST_PHONE_NUMBER_2);
-
-        // Should produce an update action
-        final Builder builder = values.buildDiff(Data.CONTENT_URI);
-        final int type = builder.build().getType();
-        assertEquals("Didn't produce update action", TYPE_UPDATE, type);
-    }
-
     public void testValuesDiffDelete() {
         final ContentValues before = new ContentValues();
         before.put(Data._ID, TEST_PHONE_ID);
diff --git a/tests/src/com/android/contacts/RawContactModifierTests.java b/tests/src/com/android/contacts/RawContactModifierTests.java
index d9f8f36..382735d 100644
--- a/tests/src/com/android/contacts/RawContactModifierTests.java
+++ b/tests/src/com/android/contacts/RawContactModifierTests.java
@@ -41,7 +41,7 @@
 import com.android.contacts.common.model.AccountTypeManager;
 import com.android.contacts.model.RawContact;
 import com.android.contacts.model.RawContactDelta;
-import com.android.contacts.model.RawContactDelta.ValuesDelta;
+import com.android.contacts.model.ValuesDelta;
 import com.android.contacts.model.RawContactDeltaList;
 import com.android.contacts.model.RawContactModifier;
 import com.android.contacts.common.model.account.AccountType;
diff --git a/tests/src/com/android/contacts/ValuesDeltaTests.java b/tests/src/com/android/contacts/ValuesDeltaTests.java
new file mode 100644
index 0000000..e9e4648
--- /dev/null
+++ b/tests/src/com/android/contacts/ValuesDeltaTests.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts;
+
+import static android.content.ContentProviderOperation.TYPE_INSERT;
+import static android.content.ContentProviderOperation.TYPE_UPDATE;
+
+import android.content.ContentProviderOperation.Builder;
+import android.content.ContentValues;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.Data;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.contacts.model.ValuesDelta;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for  {@link ValuesDelta}. These tests
+ * focus on passing changes across {@link android.os.Parcel}, and verifying that they
+ * correctly build expected "diff" operations.
+ */
+@SmallTest
+public class ValuesDeltaTests extends TestCase {
+
+    public static final long TEST_PHONE_ID = 24;
+
+    public static final String TEST_PHONE_NUMBER_1 = "218-555-1111";
+    public static final String TEST_PHONE_NUMBER_2 = "218-555-2222";
+
+    public void testValuesDiffInsert() {
+        final ContentValues after = new ContentValues();
+        after.put(Phone.NUMBER, TEST_PHONE_NUMBER_2);
+
+        final ValuesDelta values = ValuesDelta.fromAfter(after);
+
+        // Should produce an insert action
+        final Builder builder = values.buildDiff(Data.CONTENT_URI);
+        final int type = builder.build().getType();
+        assertEquals("Didn't produce insert action", TYPE_INSERT, type);
+    }
+
+    /**
+     * Test that {@link ValuesDelta#buildDiff(android.net.Uri)} is correctly
+     * built for insert, update, and delete cases. Note this only tests behavior
+     * for individual {@link Data} rows.
+     */
+    public void testValuesDiffNone() {
+        final ContentValues before = new ContentValues();
+        before.put(Data._ID, TEST_PHONE_ID);
+        before.put(Phone.NUMBER, TEST_PHONE_NUMBER_1);
+
+        final ValuesDelta values = ValuesDelta.fromBefore(before);
+
+        // None action shouldn't produce a builder
+        final Builder builder = values.buildDiff(Data.CONTENT_URI);
+        assertNull("None action produced a builder", builder);
+    }
+
+    public void testValuesDiffUpdate() {
+        final ContentValues before = new ContentValues();
+        before.put(Data._ID, TEST_PHONE_ID);
+        before.put(Phone.NUMBER, TEST_PHONE_NUMBER_1);
+
+        final ValuesDelta values = ValuesDelta.fromBefore(before);
+        values.put(Phone.NUMBER, TEST_PHONE_NUMBER_2);
+
+        // Should produce an update action
+        final Builder builder = values.buildDiff(Data.CONTENT_URI);
+        final int type = builder.build().getType();
+        assertEquals("Didn't produce update action", TYPE_UPDATE, type);
+    }
+}