Merge "Optimizing contact list scrolling"
diff --git a/src/com/android/contacts/activities/ContactBrowserActivity.java b/src/com/android/contacts/activities/ContactBrowserActivity.java
index 5695456..9167a06 100644
--- a/src/com/android/contacts/activities/ContactBrowserActivity.java
+++ b/src/com/android/contacts/activities/ContactBrowserActivity.java
@@ -299,7 +299,7 @@
         }
 
         // No editor here
-        closeEditorFragment();
+        closeEditorFragment(true);
 
         if (contactLookupUri != null) {
             // Already showing? Nothing to do
@@ -350,8 +350,14 @@
         }
     }
 
-    private void closeEditorFragment() {
+    /**
+     * Closes the editor, if it is currently open
+     * @param save Whether the changes should be saved. This should always be true, unless
+     * this is called from a Revert/Undo button
+     */
+    private void closeEditorFragment(boolean save) {
         if (mEditorFragment != null) {
+            if (save) mEditorFragment.save();
             mEditorFragment.setListener(null);
             mEditorFragment = null;
         }
@@ -369,6 +375,14 @@
         }
     }
 
+    @Override
+    protected void onPause() {
+        super.onPause();
+
+        // if anything was left unsaved, save it now
+        closeEditorFragment(true);
+    }
+
     /**
      * Creates the list fragment for the specified mode.
      */
@@ -519,22 +533,29 @@
     private class EditorFragmentListener implements ContactEditorFragment.Listener {
         @Override
         public void closeAfterDelete() {
-            Toast.makeText(ContactBrowserActivity.this, "closeAfterDelete", Toast.LENGTH_LONG).show();
+            Toast.makeText(ContactBrowserActivity.this, "closeAfterDelete",
+                    Toast.LENGTH_LONG).show();
         }
 
         @Override
         public void closeAfterRevert() {
-            Toast.makeText(ContactBrowserActivity.this, "closeAfterRevert", Toast.LENGTH_LONG).show();
+            final Uri uri = mEditorFragment.getUri();
+            closeEditorFragment(false);
+            setupContactDetailFragment(uri);
         }
 
         @Override
         public void closeAfterSaving(int resultCode, Intent resultIntent) {
-            Toast.makeText(ContactBrowserActivity.this, "closeAfterSaving", Toast.LENGTH_LONG).show();
+            // it is already saved, so no need to save again here
+            final Uri uri = mEditorFragment.getUri();
+            closeEditorFragment(false);
+            setupContactDetailFragment(uri);
         }
 
         @Override
         public void closeAfterSplit() {
-            Toast.makeText(ContactBrowserActivity.this, "closeAfterSplit", Toast.LENGTH_LONG).show();
+            Toast.makeText(ContactBrowserActivity.this, "closeAfterSplit",
+                    Toast.LENGTH_LONG).show();
         }
 
         @Override
diff --git a/src/com/android/contacts/model/EntitySet.java b/src/com/android/contacts/model/EntityDeltaList.java
similarity index 87%
rename from src/com/android/contacts/model/EntitySet.java
rename to src/com/android/contacts/model/EntityDeltaList.java
index 0f4d68d..4323e9a 100644
--- a/src/com/android/contacts/model/EntitySet.java
+++ b/src/com/android/contacts/model/EntityDeltaList.java
@@ -38,30 +38,30 @@
 /**
  * Container for multiple {@link EntityDelta} objects, usually when editing
  * together as an entire aggregate. Provides convenience methods for parceling
- * and applying another {@link EntitySet} over it.
+ * and applying another {@link EntityDeltaList} over it.
  */
-public class EntitySet extends ArrayList<EntityDelta> implements Parcelable {
+public class EntityDeltaList extends ArrayList<EntityDelta> implements Parcelable {
     private boolean mSplitRawContacts;
 
-    private EntitySet() {
+    private EntityDeltaList() {
     }
 
     /**
-     * Create an {@link EntitySet} that contains the given {@link EntityDelta},
+     * Create an {@link EntityDeltaList} that contains the given {@link EntityDelta},
      * usually when inserting a new {@link Contacts} entry.
      */
-    public static EntitySet fromSingle(EntityDelta delta) {
-        final EntitySet state = new EntitySet();
+    public static EntityDeltaList fromSingle(EntityDelta delta) {
+        final EntityDeltaList state = new EntityDeltaList();
         state.add(delta);
         return state;
     }
 
     /**
-     * Create an {@link EntitySet} based on {@link Contacts} specified by the
+     * Create an {@link EntityDeltaList} based on {@link Contacts} specified by the
      * given query parameters. This closes the {@link EntityIterator} when
      * finished, so it doesn't subscribe to updates.
      */
-    public static EntitySet fromQuery(ContentResolver resolver, String selection,
+    public static EntityDeltaList fromQuery(ContentResolver resolver, String selection,
             String[] selectionArgs, String sortOrder) {
         final EntityIterator iterator = RawContacts.newEntityIterator(resolver.query(
                 RawContactsEntity.CONTENT_URI, null, selection, selectionArgs,
@@ -74,10 +74,11 @@
     }
 
     /**
-     * Create an {@link EntitySet} that contains the entities of the Iterator as before values.
+     * Create an {@link EntityDeltaList} that contains the entities of the Iterator as before
+     * values.
      */
-    public static EntitySet fromIterator(Iterator<Entity> iterator) {
-        final EntitySet state = new EntitySet();
+    public static EntityDeltaList fromIterator(Iterator<Entity> iterator) {
+        final EntityDeltaList state = new EntityDeltaList();
         // Perform background query to pull contact details
         while (iterator.hasNext()) {
             // Read all contacts into local deltas to prepare for edits
@@ -89,12 +90,12 @@
     }
 
     /**
-     * Merge the "after" values from the given {@link EntitySet}, discarding any
+     * Merge the "after" values from the given {@link EntityDeltaList}, discarding any
      * previous "after" states. This is typically used when re-parenting user
-     * edits onto an updated {@link EntitySet}.
+     * edits onto an updated {@link EntityDeltaList}.
      */
-    public static EntitySet mergeAfter(EntitySet local, EntitySet remote) {
-        if (local == null) local = new EntitySet();
+    public static EntityDeltaList mergeAfter(EntityDeltaList local, EntityDeltaList remote) {
+        if (local == null) local = new EntityDeltaList();
 
         // For each entity in the remote set, try matching over existing
         for (EntityDelta remoteEntity : remote) {
@@ -161,7 +162,8 @@
             } else {
                 // Additional insert case, so point at first insert
                 final Builder builder = beginKeepTogether();
-                builder.withValueBackReference(AggregationExceptions.RAW_CONTACT_ID1, firstInsertRow);
+                builder.withValueBackReference(AggregationExceptions.RAW_CONTACT_ID1,
+                        firstInsertRow);
                 builder.withValueBackReference(AggregationExceptions.RAW_CONTACT_ID2, firstBatch);
                 diff.add(builder.build());
             }
@@ -331,15 +333,16 @@
         }
     }
 
-    public static final Parcelable.Creator<EntitySet> CREATOR = new Parcelable.Creator<EntitySet>() {
-        public EntitySet createFromParcel(Parcel in) {
-            final EntitySet state = new EntitySet();
+    public static final Parcelable.Creator<EntityDeltaList> CREATOR =
+            new Parcelable.Creator<EntityDeltaList>() {
+        public EntityDeltaList createFromParcel(Parcel in) {
+            final EntityDeltaList state = new EntityDeltaList();
             state.readFromParcel(in);
             return state;
         }
 
-        public EntitySet[] newArray(int size) {
-            return new EntitySet[size];
+        public EntityDeltaList[] newArray(int size) {
+            return new EntityDeltaList[size];
         }
     };
 }
diff --git a/src/com/android/contacts/model/EntityDiff.java b/src/com/android/contacts/model/EntityDiff.java
deleted file mode 100644
index ea46567..0000000
--- a/src/com/android/contacts/model/EntityDiff.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2009 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.content.Entity;
-import android.content.ContentProviderOperation.Builder;
-import android.content.Entity.NamedContentValues;
-import android.net.Uri;
-import android.provider.BaseColumns;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-
-
-/**
- * Describes a set of {@link ContentProviderOperation} that need to be
- * executed to transform a database from one {@link Entity} to another.
- */
-@Deprecated
-public class EntityDiff extends ArrayList<ContentProviderOperation> {
-    private EntityDiff() {
-    }
-
-    /**
-     * Build the set of {@link ContentProviderOperation} needed to translate
-     * from "before" to "after". Tries its best to keep operations to
-     * minimal number required. Assumes that all {@link ContentValues} are
-     * keyed using {@link BaseColumns#_ID} values.
-     */
-    public static EntityDiff buildDiff(Entity before, Entity after, Uri targetUri,
-            String childForeignKey) {
-        final EntityDiff diff = new EntityDiff();
-
-        Builder builder;
-        ContentValues values;
-
-        if (before == null) {
-            // Before doesn't exist, so insert "after" values
-            builder = ContentProviderOperation.newInsert(targetUri);
-            builder.withValues(after.getEntityValues());
-            diff.add(builder.build());
-
-            for (NamedContentValues child : after.getSubValues()) {
-                // Add builder with reference to original _id when needed
-                builder = ContentProviderOperation.newInsert(child.uri);
-                builder.withValues(child.values);
-                if (childForeignKey != null) {
-                    builder.withValueBackReference(childForeignKey, 0);
-                }
-                diff.add(builder.build());
-            }
-
-        } else if (after == null) {
-            // After doesn't exist, so delete "before" values
-            for (NamedContentValues child : before.getSubValues()) {
-                builder = ContentProviderOperation.newDelete(child.uri);
-                builder.withSelection(getSelectIdClause(child.values), null);
-                diff.add(builder.build());
-            }
-
-            builder = ContentProviderOperation.newDelete(targetUri);
-            builder.withSelection(getSelectIdClause(before.getEntityValues()), null);
-            diff.add(builder.build());
-
-        } else {
-            // Somewhere between, so update any changed values
-            values = after.getEntityValues();
-            if (!before.getEntityValues().equals(values)) {
-                // Top-level values changed, so update
-                builder = ContentProviderOperation.newUpdate(targetUri);
-                builder.withSelection(getSelectIdClause(values), null);
-                builder.withValues(values);
-                diff.add(builder.build());
-            }
-
-            // Build lookup maps for children on both sides
-            final HashMap<String, NamedContentValues> beforeChildren = buildChildrenMap(before);
-            final HashMap<String, NamedContentValues> afterChildren = buildChildrenMap(after);
-
-            // Walk through "before" children looking for deletes and updates
-            for (NamedContentValues beforeChild : beforeChildren.values()) {
-                final String key = buildChildKey(beforeChild);
-                final NamedContentValues afterChild = afterChildren.get(key);
-
-                if (afterChild == null) {
-                    // After child doesn't exist, so delete "before" child
-                    builder = ContentProviderOperation.newDelete(beforeChild.uri);
-                    builder.withSelection(getSelectIdClause(beforeChild.values), null);
-                    diff.add(builder.build());
-                } else if (!beforeChild.values.equals(afterChild.values)) {
-                    // After child still exists, and is different, so update
-                    values = afterChild.values;
-                    builder = ContentProviderOperation.newUpdate(afterChild.uri);
-                    builder.withSelection(getSelectIdClause(values), null);
-                    builder.withValues(values);
-                    diff.add(builder.build());
-                }
-
-                // Remove the now-handled "after" child
-                afterChildren.remove(key);
-            }
-
-            // Walk through remaining "after" children, which are inserts
-            for (NamedContentValues afterChild : afterChildren.values()) {
-                builder = ContentProviderOperation.newInsert(afterChild.uri);
-                builder.withValues(afterChild.values);
-                diff.add(builder.build());
-            }
-        }
-
-        return diff;
-    }
-
-    private static String buildChildKey(NamedContentValues child) {
-        return child.uri.toString() + child.values.getAsString(BaseColumns._ID);
-    }
-
-    private static String getSelectIdClause(ContentValues values) {
-        return BaseColumns._ID + "=" + values.getAsLong(BaseColumns._ID);
-    }
-
-    private static HashMap<String, NamedContentValues> buildChildrenMap(Entity entity) {
-        final ArrayList<NamedContentValues> children = entity.getSubValues();
-        final HashMap<String, NamedContentValues> childrenMap = new HashMap<String, NamedContentValues>(
-                children.size());
-        for (NamedContentValues child : children) {
-            final String key = buildChildKey(child);
-            childrenMap.put(key, child);
-        }
-        return childrenMap;
-    }
-}
diff --git a/src/com/android/contacts/model/EntityModifier.java b/src/com/android/contacts/model/EntityModifier.java
index 2e6899e..37267ec 100644
--- a/src/com/android/contacts/model/EntityModifier.java
+++ b/src/com/android/contacts/model/EntityModifier.java
@@ -348,11 +348,11 @@
 
     /**
      * Processing to trim any empty {@link ValuesDelta} and {@link EntityDelta}
-     * from the given {@link EntitySet}, assuming the given {@link Sources}
+     * from the given {@link EntityDeltaList}, assuming the given {@link Sources}
      * dictates the structure for various fields. This method ignores rows not
      * described by the {@link ContactsSource}.
      */
-    public static void trimEmpty(EntitySet set, Sources sources) {
+    public static void trimEmpty(EntityDeltaList set, Sources sources) {
         for (EntityDelta state : set) {
             final String accountType = state.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
             final ContactsSource source = sources.getInflatedSource(accountType,
diff --git a/src/com/android/contacts/views/editor/ContactEditorFragment.java b/src/com/android/contacts/views/editor/ContactEditorFragment.java
index 7418e8b..695e989 100644
--- a/src/com/android/contacts/views/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/views/editor/ContactEditorFragment.java
@@ -22,7 +22,7 @@
 import com.android.contacts.model.Editor;
 import com.android.contacts.model.EntityDelta;
 import com.android.contacts.model.EntityModifier;
-import com.android.contacts.model.EntitySet;
+import com.android.contacts.model.EntityDeltaList;
 import com.android.contacts.model.GoogleSource;
 import com.android.contacts.model.Sources;
 import com.android.contacts.model.ContactsSource.EditType;
@@ -40,7 +40,6 @@
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.LoaderManagingFragment;
-import android.app.ProgressDialog;
 import android.content.ActivityNotFoundException;
 import android.content.ContentProviderOperation;
 import android.content.ContentProviderResult;
@@ -84,20 +83,12 @@
 import android.widget.Toast;
 
 import java.io.File;
-import java.lang.ref.WeakReference;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Date;
 
-//Here are the open TODOs for the Fragment transition
-//TODO How to save data? Service?
-//TODO Do account-list lookup always in a thread
-//TODO Cleanup state handling (orientation changes etc).
-//TODO Cleanup the load function. It can currenlty also do insert, which is awkward
-//TODO Watch for background changes...How?
-
 public class ContactEditorFragment extends LoaderManagingFragment<ContactLoader.Result> {
 
     private static final String TAG = "ContactEditorFragment";
@@ -144,7 +135,7 @@
     private long mContactIdForJoin;
 
     private LinearLayout mContent;
-    private EntitySet mState;
+    private EntityDeltaList mState;
 
     private ViewIdGenerator mViewIdGenerator;
 
@@ -228,7 +219,7 @@
             mViewIdGenerator = new ViewIdGenerator();
         } else {
             // Read modifications from instance
-            mState = savedState.<EntitySet> getParcelable(KEY_EDIT_STATE);
+            mState = savedState.<EntityDeltaList> getParcelable(KEY_EDIT_STATE);
             mRawContactIdRequestingPhoto = savedState.getLong(
                     KEY_RAW_CONTACT_ID_REQUESTING_PHOTO);
             mViewIdGenerator = savedState.getParcelable(KEY_VIEW_ID_GENERATOR);
@@ -260,7 +251,7 @@
     }
 
     public void setData(ContactLoader.Result data) {
-        mState = EntitySet.fromIterator(data.getEntities().iterator());
+        mState = EntityDeltaList.fromIterator(data.getEntities().iterator());
         // TODO: Merge in Intent parameters can only be done on the first load.
         // The behaviour for subsequent loads is probably broken, so fix this
         final boolean hasExtras = mIntentExtras != null && mIntentExtras.size() > 0;
@@ -327,7 +318,7 @@
 
         if (mState == null) {
             // Create state if none exists yet
-            mState = EntitySet.fromSingle(insert);
+            mState = EntityDeltaList.fromSingle(insert);
         } else {
             // Add contact onto end of existing state
             mState.add(insert);
@@ -775,6 +766,14 @@
         return true;
     }
 
+    /**
+     * Asynchonously saves the changes made by the user. This can be called even if nothing
+     * has changed
+     */
+    public void save() {
+        doSaveAction(SAVE_MODE_DEFAULT);
+    }
+
     private boolean doRevertAction() {
         if (mListener != null) mListener.closeAfterRevert();
 
@@ -1017,6 +1016,9 @@
 
             int value;
             if (!skipAccountTypeCheck) {
+                if (oneSource.accountType == null) {
+                    return 1;
+                }
                 value = oneSource.accountType.compareTo(twoSource.accountType);
                 if (value != 0) {
                     return value;
@@ -1170,14 +1172,13 @@
      * persisting in cases where the system wants to reclaim our process.
      */
     public static class PersistTask extends
-            WeakAsyncTask<EntitySet, Void, Integer, ContactEditorFragment> {
+            WeakAsyncTask<EntityDeltaList, Void, Integer, ContactEditorFragment> {
         private static final int PERSIST_TRIES = 3;
 
         private static final int RESULT_UNCHANGED = 0;
         private static final int RESULT_SUCCESS = 1;
         private static final int RESULT_FAILURE = 2;
 
-        private WeakReference<ProgressDialog> mProgress;
         private final Context mContext;
 
         private int mSaveMode;
@@ -1192,9 +1193,6 @@
         /** {@inheritDoc} */
         @Override
         protected void onPreExecute(ContactEditorFragment target) {
-            mProgress = new WeakReference<ProgressDialog>(ProgressDialog.show(mContext, null,
-                    mContext.getText(R.string.savingContact)));
-
             // Before starting this task, start an empty service to protect our
             // process from being reclaimed by the system.
             mContext.startService(new Intent(mContext, EmptyService.class));
@@ -1202,10 +1200,10 @@
 
         /** {@inheritDoc} */
         @Override
-        protected Integer doInBackground(ContactEditorFragment target, EntitySet... params) {
+        protected Integer doInBackground(ContactEditorFragment target, EntityDeltaList... params) {
             final ContentResolver resolver = mContext.getContentResolver();
 
-            EntitySet state = params[0];
+            EntityDeltaList state = params[0];
 
             // Trim any empty fields, and RawContacts, before persisting
             final Sources sources = Sources.getInstance(mContext);
@@ -1243,16 +1241,16 @@
                 } catch (OperationApplicationException e) {
                     // Version consistency failed, re-parent change and try again
                     Log.w(TAG, "Version consistency failed, re-parenting: " + e.toString());
-                    final EntitySet newState = EntitySet.fromQuery(resolver,
+                    final EntityDeltaList newState = EntityDeltaList.fromQuery(resolver,
                             target.mQuerySelection, target.mQuerySelectionArgs, null);
-                    state = EntitySet.mergeAfter(newState, state);
+                    state = EntityDeltaList.mergeAfter(newState, state);
                 }
             }
 
             return result;
         }
 
-        private long getRawContactId(EntitySet state,
+        private long getRawContactId(EntityDeltaList state,
                 final ArrayList<ContentProviderOperation> diff,
                 final ContentProviderResult[] results) {
             long rawContactId = state.findRawContactId();
@@ -1276,16 +1274,12 @@
         /** {@inheritDoc} */
         @Override
         protected void onPostExecute(ContactEditorFragment target, Integer result) {
-            final ProgressDialog progress = mProgress.get();
-
             if (result == RESULT_SUCCESS && mSaveMode != SAVE_MODE_JOIN) {
                 Toast.makeText(mContext, R.string.contactSavedToast, Toast.LENGTH_SHORT).show();
             } else if (result == RESULT_FAILURE) {
                 Toast.makeText(mContext, R.string.contactSavedErrorToast, Toast.LENGTH_LONG).show();
             }
 
-            if (progress != null) progress.dismiss();
-
             // Stop the service that was protecting us
             mContext.stopService(new Intent(mContext, EmptyService.class));
 
@@ -1360,4 +1354,4 @@
     public Uri getUri() {
         return mUri;
     }
-}
\ No newline at end of file
+}
diff --git a/tests/src/com/android/contacts/EntitySetTests.java b/tests/src/com/android/contacts/EntityDeltaListTests.java
similarity index 86%
rename from tests/src/com/android/contacts/EntitySetTests.java
rename to tests/src/com/android/contacts/EntityDeltaListTests.java
index edfca6d..efd843f 100644
--- a/tests/src/com/android/contacts/EntitySetTests.java
+++ b/tests/src/com/android/contacts/EntityDeltaListTests.java
@@ -25,7 +25,7 @@
 import com.android.contacts.model.ContactsSource;
 import com.android.contacts.model.EntityDelta;
 import com.android.contacts.model.EntityModifier;
-import com.android.contacts.model.EntitySet;
+import com.android.contacts.model.EntityDeltaList;
 import com.android.contacts.model.EntityDelta.ValuesDelta;
 import com.google.android.collect.Lists;
 
@@ -46,12 +46,12 @@
 import java.util.ArrayList;
 
 /**
- * Tests for {@link EntitySet} which focus on "diff" operations that should
+ * Tests for {@link EntityDeltaList} which focus on "diff" operations that should
  * create {@link AggregationExceptions} in certain cases.
  */
 @LargeTest
-public class EntitySetTests extends AndroidTestCase {
-    public static final String TAG = "EntitySetTests";
+public class EntityDeltaListTests extends AndroidTestCase {
+    public static final String TAG = "EntityDeltaListTests";
 
     private static final long CONTACT_FIRST = 1;
     private static final long CONTACT_SECOND = 2;
@@ -71,7 +71,7 @@
     public static final String TEST_PHONE = "555-1212";
     public static final String TEST_ACCOUNT = "org.example.test";
 
-    public EntitySetTests() {
+    public EntityDeltaListTests() {
         super();
     }
 
@@ -112,8 +112,8 @@
         return new EntityDelta(values);
     }
 
-    static EntitySet buildSet(EntityDelta... deltas) {
-        final EntitySet set = EntitySet.fromSingle(deltas[0]);
+    static EntityDeltaList buildSet(EntityDelta... deltas) {
+        final EntityDeltaList set = EntityDeltaList.fromSingle(deltas[0]);
         for (int i = 1; i < deltas.length; i++) {
             set.add(deltas[i]);
         }
@@ -166,12 +166,12 @@
         return values;
     }
 
-    static void insertPhone(EntitySet set, long rawContactId, ContentValues values) {
+    static void insertPhone(EntityDeltaList set, long rawContactId, ContentValues values) {
         final EntityDelta match = set.getByRawContactId(rawContactId);
         match.addEntry(ValuesDelta.fromAfter(values));
     }
 
-    static ValuesDelta getPhone(EntitySet set, long rawContactId, long dataId) {
+    static ValuesDelta getPhone(EntityDeltaList set, long rawContactId, long dataId) {
         final EntityDelta match = set.getByRawContactId(rawContactId);
         return match.getEntry(dataId);
     }
@@ -183,7 +183,7 @@
         assertDiffPattern(diff, pattern);
     }
 
-    static void assertDiffPattern(EntitySet set, ContentProviderOperation... pattern) {
+    static void assertDiffPattern(EntityDeltaList set, ContentProviderOperation... pattern) {
         assertDiffPattern(set.buildDiff(), pattern);
     }
 
@@ -283,7 +283,7 @@
         return null;
     }
 
-    static Long getVersion(EntitySet set, Long rawContactId) {
+    static Long getVersion(EntityDeltaList set, Long rawContactId) {
         return set.getByRawContactId(rawContactId).getValues().getAsLong(RawContacts.VERSION);
     }
 
@@ -304,7 +304,7 @@
 
     public void testInsert() {
         final EntityDelta insert = getInsert();
-        final EntitySet set = buildSet(insert);
+        final EntityDeltaList set = buildSet(insert);
 
         // Inserting single shouldn't create rules
         final ArrayList<ContentProviderOperation> diff = set.buildDiff();
@@ -315,7 +315,7 @@
     public void testUpdateUpdate() {
         final EntityDelta updateFirst = getUpdate(CONTACT_FIRST);
         final EntityDelta updateSecond = getUpdate(CONTACT_SECOND);
-        final EntitySet set = buildSet(updateFirst, updateSecond);
+        final EntityDeltaList set = buildSet(updateFirst, updateSecond);
 
         // Updating two existing shouldn't create rules
         final ArrayList<ContentProviderOperation> diff = set.buildDiff();
@@ -326,7 +326,7 @@
     public void testUpdateInsert() {
         final EntityDelta update = getUpdate(CONTACT_FIRST);
         final EntityDelta insert = getInsert();
-        final EntitySet set = buildSet(update, insert);
+        final EntityDeltaList set = buildSet(update, insert);
 
         // New insert should only create one rule
         final ArrayList<ContentProviderOperation> diff = set.buildDiff();
@@ -338,7 +338,7 @@
         final EntityDelta insertFirst = getInsert();
         final EntityDelta update = getUpdate(CONTACT_FIRST);
         final EntityDelta insertSecond = getInsert();
-        final EntitySet set = buildSet(insertFirst, update, insertSecond);
+        final EntityDeltaList set = buildSet(insertFirst, update, insertSecond);
 
         // Two inserts should create two rules to bind against single existing
         final ArrayList<ContentProviderOperation> diff = set.buildDiff();
@@ -350,7 +350,7 @@
         final EntityDelta insertFirst = getInsert();
         final EntityDelta insertSecond = getInsert();
         final EntityDelta insertThird = getInsert();
-        final EntitySet set = buildSet(insertFirst, insertSecond, insertThird);
+        final EntityDeltaList set = buildSet(insertFirst, insertSecond, insertThird);
 
         // Three new inserts should create only two binding rules
         final ArrayList<ContentProviderOperation> diff = set.buildDiff();
@@ -359,20 +359,20 @@
     }
 
     public void testMergeDataRemoteInsert() {
-        final EntitySet first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
+        final EntityDeltaList first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
                 buildPhone(PHONE_RED)));
-        final EntitySet second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
+        final EntityDeltaList second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
                 buildPhone(PHONE_RED), buildPhone(PHONE_GREEN)));
 
         // Merge in second version, verify they match
-        final EntitySet merged = EntitySet.mergeAfter(second, first);
+        final EntityDeltaList merged = EntityDeltaList.mergeAfter(second, first);
         assertEquals("Unexpected change when merging", second, merged);
     }
 
     public void testMergeDataLocalUpdateRemoteInsert() {
-        final EntitySet first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
+        final EntityDeltaList first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
                 buildPhone(PHONE_RED)));
-        final EntitySet second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
+        final EntityDeltaList second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
                 buildPhone(PHONE_RED), buildPhone(PHONE_GREEN)));
 
         // Change the local number to trigger update
@@ -386,7 +386,7 @@
                 buildUpdateAggregationDefault());
 
         // Merge in the second version, verify diff matches
-        final EntitySet merged = EntitySet.mergeAfter(second, first);
+        final EntityDeltaList merged = EntityDeltaList.mergeAfter(second, first);
         assertDiffPattern(merged,
                 buildAssertVersion(VER_SECOND),
                 buildUpdateAggregationSuspended(),
@@ -395,9 +395,9 @@
     }
 
     public void testMergeDataLocalUpdateRemoteDelete() {
-        final EntitySet first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
+        final EntityDeltaList first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
                 buildPhone(PHONE_RED)));
-        final EntitySet second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
+        final EntityDeltaList second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
                 buildPhone(PHONE_GREEN)));
 
         // Change the local number to trigger update
@@ -412,7 +412,7 @@
 
         // Merge in the second version, verify that our update changed to
         // insert, since RED was deleted on remote side
-        final EntitySet merged = EntitySet.mergeAfter(second, first);
+        final EntityDeltaList merged = EntityDeltaList.mergeAfter(second, first);
         assertDiffPattern(merged,
                 buildAssertVersion(VER_SECOND),
                 buildUpdateAggregationSuspended(),
@@ -421,9 +421,9 @@
     }
 
     public void testMergeDataLocalDeleteRemoteUpdate() {
-        final EntitySet first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
+        final EntityDeltaList first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
                 buildPhone(PHONE_RED)));
-        final EntitySet second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
+        final EntityDeltaList second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
                 buildPhone(PHONE_RED, TEST_PHONE)));
 
         // Delete phone locally
@@ -437,7 +437,7 @@
                 buildUpdateAggregationDefault());
 
         // Merge in the second version, verify that our delete remains
-        final EntitySet merged = EntitySet.mergeAfter(second, first);
+        final EntityDeltaList merged = EntityDeltaList.mergeAfter(second, first);
         assertDiffPattern(merged,
                 buildAssertVersion(VER_SECOND),
                 buildUpdateAggregationSuspended(),
@@ -446,9 +446,9 @@
     }
 
     public void testMergeDataLocalInsertRemoteInsert() {
-        final EntitySet first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
+        final EntityDeltaList first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
                 buildPhone(PHONE_RED)));
-        final EntitySet second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
+        final EntityDeltaList second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
                 buildPhone(PHONE_RED), buildPhone(PHONE_GREEN)));
 
         // Insert new phone locally
@@ -461,7 +461,7 @@
                 buildUpdateAggregationDefault());
 
         // Merge in the second version, verify that our insert remains
-        final EntitySet merged = EntitySet.mergeAfter(second, first);
+        final EntityDeltaList merged = EntityDeltaList.mergeAfter(second, first);
         assertDiffPattern(merged,
                 buildAssertVersion(VER_SECOND),
                 buildUpdateAggregationSuspended(),
@@ -470,9 +470,9 @@
     }
 
     public void testMergeRawContactLocalInsertRemoteInsert() {
-        final EntitySet first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
+        final EntityDeltaList first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
                 buildPhone(PHONE_RED)));
-        final EntitySet second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
+        final EntityDeltaList second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
                 buildPhone(PHONE_RED)), buildBeforeEntity(CONTACT_MARY, VER_SECOND,
                 buildPhone(PHONE_RED)));
 
@@ -490,7 +490,7 @@
                 buildUpdateAggregationKeepTogether(CONTACT_BOB));
 
         // Merge in the second version, verify that our insert remains
-        final EntitySet merged = EntitySet.mergeAfter(second, first);
+        final EntityDeltaList merged = EntityDeltaList.mergeAfter(second, first);
         assertDiffPattern(merged,
                 buildAssertVersion(VER_SECOND),
                 buildAssertVersion(VER_SECOND),
@@ -501,10 +501,10 @@
     }
 
     public void testMergeRawContactLocalDeleteRemoteDelete() {
-        final EntitySet first = buildSet(
+        final EntityDeltaList first = buildSet(
                 buildBeforeEntity(CONTACT_BOB, VER_FIRST, buildPhone(PHONE_RED)),
                 buildBeforeEntity(CONTACT_MARY, VER_FIRST, buildPhone(PHONE_RED)));
-        final EntitySet second = buildSet(
+        final EntityDeltaList second = buildSet(
                 buildBeforeEntity(CONTACT_BOB, VER_SECOND, buildPhone(PHONE_RED)));
 
         // Remove contact locally
@@ -515,15 +515,15 @@
                 buildDelete(RawContacts.CONTENT_URI));
 
         // Merge in the second version, verify that our delete isn't needed
-        final EntitySet merged = EntitySet.mergeAfter(second, first);
+        final EntityDeltaList merged = EntityDeltaList.mergeAfter(second, first);
         assertDiffPattern(merged);
     }
 
     public void testMergeRawContactLocalUpdateRemoteDelete() {
-        final EntitySet first = buildSet(
+        final EntityDeltaList first = buildSet(
                 buildBeforeEntity(CONTACT_BOB, VER_FIRST, buildPhone(PHONE_RED)),
                 buildBeforeEntity(CONTACT_MARY, VER_FIRST, buildPhone(PHONE_RED)));
-        final EntitySet second = buildSet(
+        final EntityDeltaList second = buildSet(
                 buildBeforeEntity(CONTACT_BOB, VER_SECOND, buildPhone(PHONE_RED)));
 
         // Perform local update
@@ -542,7 +542,7 @@
         contactInsert.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED);
 
         // Merge and verify that update turned into insert
-        final EntitySet merged = EntitySet.mergeAfter(second, first);
+        final EntityDeltaList merged = EntityDeltaList.mergeAfter(second, first);
         assertDiffPattern(merged,
                 buildAssertVersion(VER_SECOND),
                 buildOper(RawContacts.CONTENT_URI, TYPE_INSERT, contactInsert),
@@ -552,22 +552,22 @@
     }
 
     public void testMergeUsesNewVersion() {
-        final EntitySet first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
+        final EntityDeltaList first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
                 buildPhone(PHONE_RED)));
-        final EntitySet second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
+        final EntityDeltaList second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
                 buildPhone(PHONE_RED)));
 
         assertEquals((Long)VER_FIRST, getVersion(first, CONTACT_BOB));
         assertEquals((Long)VER_SECOND, getVersion(second, CONTACT_BOB));
 
-        final EntitySet merged = EntitySet.mergeAfter(second, first);
+        final EntityDeltaList merged = EntityDeltaList.mergeAfter(second, first);
         assertEquals((Long)VER_SECOND, getVersion(merged, CONTACT_BOB));
     }
 
     public void testMergeAfterEnsureAndTrim() {
-        final EntitySet first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
+        final EntityDeltaList first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
                 buildEmail(EMAIL_YELLOW)));
-        final EntitySet second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
+        final EntityDeltaList second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
                 buildEmail(EMAIL_YELLOW)));
 
         // Ensure we have at least one phone
@@ -588,7 +588,7 @@
         assertDiffPattern(first);
 
         // Now re-parent the change, which should remain no-op
-        final EntitySet merged = EntitySet.mergeAfter(second, first);
+        final EntityDeltaList merged = EntityDeltaList.mergeAfter(second, first);
         assertDiffPattern(merged);
     }
 }
diff --git a/tests/src/com/android/contacts/EntityModifierTests.java b/tests/src/com/android/contacts/EntityModifierTests.java
index 18877a3..af36815 100644
--- a/tests/src/com/android/contacts/EntityModifierTests.java
+++ b/tests/src/com/android/contacts/EntityModifierTests.java
@@ -23,7 +23,7 @@
 import com.android.contacts.model.ContactsSource;
 import com.android.contacts.model.EntityDelta;
 import com.android.contacts.model.EntityModifier;
-import com.android.contacts.model.EntitySet;
+import com.android.contacts.model.EntityDeltaList;
 import com.android.contacts.model.Sources;
 import com.android.contacts.model.ContactsSource.DataKind;
 import com.android.contacts.model.ContactsSource.EditType;
@@ -376,23 +376,23 @@
         final EditType typeHome = EntityModifier.getType(kindPhone, Phone.TYPE_HOME);
 
         // Test row that has type values, but values are spaces
-        final EntityDelta state = EntitySetTests.buildBeforeEntity(TEST_ID, VER_FIRST);
+        final EntityDelta state = EntityDeltaListTests.buildBeforeEntity(TEST_ID, VER_FIRST);
         final ValuesDelta values = EntityModifier.insertChild(state, kindPhone, typeHome);
         values.put(Phone.NUMBER, "   ");
 
         // Build diff, expecting insert for data row and update enforcement
-        EntitySetTests.assertDiffPattern(state,
-                EntitySetTests.buildAssertVersion(VER_FIRST),
-                EntitySetTests.buildUpdateAggregationSuspended(),
-                EntitySetTests.buildOper(Data.CONTENT_URI, TYPE_INSERT,
-                        EntitySetTests.buildDataInsert(values, TEST_ID)),
-                EntitySetTests.buildUpdateAggregationDefault());
+        EntityDeltaListTests.assertDiffPattern(state,
+                EntityDeltaListTests.buildAssertVersion(VER_FIRST),
+                EntityDeltaListTests.buildUpdateAggregationSuspended(),
+                EntityDeltaListTests.buildOper(Data.CONTENT_URI, TYPE_INSERT,
+                        EntityDeltaListTests.buildDataInsert(values, TEST_ID)),
+                EntityDeltaListTests.buildUpdateAggregationDefault());
 
         // Trim empty rows and try again, expecting delete of overall contact
         EntityModifier.trimEmpty(state, source);
-        EntitySetTests.assertDiffPattern(state,
-                EntitySetTests.buildAssertVersion(VER_FIRST),
-                EntitySetTests.buildDelete(RawContacts.CONTENT_URI));
+        EntityDeltaListTests.assertDiffPattern(state,
+                EntityDeltaListTests.buildAssertVersion(VER_FIRST),
+                EntityDeltaListTests.buildDelete(RawContacts.CONTENT_URI));
     }
 
     public void testTrimLeaveValid() {
@@ -401,26 +401,26 @@
         final EditType typeHome = EntityModifier.getType(kindPhone, Phone.TYPE_HOME);
 
         // Test row that has type values with valid number
-        final EntityDelta state = EntitySetTests.buildBeforeEntity(TEST_ID, VER_FIRST);
+        final EntityDelta state = EntityDeltaListTests.buildBeforeEntity(TEST_ID, VER_FIRST);
         final ValuesDelta values = EntityModifier.insertChild(state, kindPhone, typeHome);
         values.put(Phone.NUMBER, TEST_PHONE);
 
         // Build diff, expecting insert for data row and update enforcement
-        EntitySetTests.assertDiffPattern(state,
-                EntitySetTests.buildAssertVersion(VER_FIRST),
-                EntitySetTests.buildUpdateAggregationSuspended(),
-                EntitySetTests.buildOper(Data.CONTENT_URI, TYPE_INSERT,
-                        EntitySetTests.buildDataInsert(values, TEST_ID)),
-                EntitySetTests.buildUpdateAggregationDefault());
+        EntityDeltaListTests.assertDiffPattern(state,
+                EntityDeltaListTests.buildAssertVersion(VER_FIRST),
+                EntityDeltaListTests.buildUpdateAggregationSuspended(),
+                EntityDeltaListTests.buildOper(Data.CONTENT_URI, TYPE_INSERT,
+                        EntityDeltaListTests.buildDataInsert(values, TEST_ID)),
+                EntityDeltaListTests.buildUpdateAggregationDefault());
 
         // Trim empty rows and try again, expecting no differences
         EntityModifier.trimEmpty(state, source);
-        EntitySetTests.assertDiffPattern(state,
-                EntitySetTests.buildAssertVersion(VER_FIRST),
-                EntitySetTests.buildUpdateAggregationSuspended(),
-                EntitySetTests.buildOper(Data.CONTENT_URI, TYPE_INSERT,
-                        EntitySetTests.buildDataInsert(values, TEST_ID)),
-                EntitySetTests.buildUpdateAggregationDefault());
+        EntityDeltaListTests.assertDiffPattern(state,
+                EntityDeltaListTests.buildAssertVersion(VER_FIRST),
+                EntityDeltaListTests.buildUpdateAggregationSuspended(),
+                EntityDeltaListTests.buildOper(Data.CONTENT_URI, TYPE_INSERT,
+                        EntityDeltaListTests.buildDataInsert(values, TEST_ID)),
+                EntityDeltaListTests.buildUpdateAggregationDefault());
     }
 
     public void testTrimEmptyUntouched() {
@@ -507,7 +507,7 @@
 
         // Try creating a contact without any child entries
         final EntityDelta state = getEntity(null);
-        final EntitySet set = EntitySet.fromSingle(state);
+        final EntityDeltaList set = EntityDeltaList.fromSingle(state);
 
         // Build diff, expecting single insert
         final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
@@ -535,7 +535,7 @@
         // Try creating a contact with single empty entry
         final EntityDelta state = getEntity(null);
         final ValuesDelta values = EntityModifier.insertChild(state, kindPhone, typeHome);
-        final EntitySet set = EntitySet.fromSingle(state);
+        final EntityDeltaList set = EntityDeltaList.fromSingle(state);
 
         // Build diff, expecting two insert operations
         final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
@@ -579,7 +579,7 @@
         second.put(Phone.NUMBER, TEST_PHONE);
 
         final EntityDelta state = getEntity(TEST_ID, first, second);
-        final EntitySet set = EntitySet.fromSingle(state);
+        final EntityDeltaList set = EntityDeltaList.fromSingle(state);
 
         // Build diff, expecting no changes
         final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
@@ -644,7 +644,7 @@
         first.put(Phone.NUMBER, TEST_PHONE);
 
         final EntityDelta state = getEntity(TEST_ID, first);
-        final EntitySet set = EntitySet.fromSingle(state);
+        final EntityDeltaList set = EntityDeltaList.fromSingle(state);
 
         // Build diff, expecting no changes
         final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();