Moving contact saving to the service
Bug: 3220304
Bug: 3452739
Change-Id: If4124096a24e5dd302feb5338efaaa8398b2cb6b
diff --git a/src/com/android/contacts/ContactSaveService.java b/src/com/android/contacts/ContactSaveService.java
index c07dd68..f7ede90 100644
--- a/src/com/android/contacts/ContactSaveService.java
+++ b/src/com/android/contacts/ContactSaveService.java
@@ -16,7 +16,9 @@
package com.android.contacts;
-import com.android.contacts.R;
+import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.EntityDeltaList;
+import com.android.contacts.model.EntityModifier;
import com.google.android.collect.Lists;
import com.google.android.collect.Sets;
@@ -35,6 +37,7 @@
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
+import android.os.Parcelable;
import android.os.RemoteException;
import android.provider.ContactsContract;
import android.provider.ContactsContract.AggregationExceptions;
@@ -63,7 +66,9 @@
public static final String EXTRA_CONTENT_VALUES = "contentValues";
public static final String EXTRA_CALLBACK_INTENT = "callbackIntent";
- public static final String EXTRA_OPERATIONS = "Operations";
+ public static final String ACTION_SAVE_CONTACT = "saveContact";
+ public static final String EXTRA_CONTACT_STATE = "state";
+ public static final String EXTRA_SAVE_MODE = "saveMode";
public static final String ACTION_CREATE_GROUP = "createGroup";
public static final String ACTION_RENAME_GROUP = "renameGroup";
@@ -105,16 +110,30 @@
Data.DATA15
);
+ private static final int PERSIST_TRIES = 3;
+
public ContactSaveService() {
super(TAG);
setIntentRedelivery(true);
}
@Override
+ public Object getSystemService(String name) {
+ Object service = super.getSystemService(name);
+ if (service != null) {
+ return service;
+ }
+
+ return getApplicationContext().getSystemService(name);
+ }
+
+ @Override
protected void onHandleIntent(Intent intent) {
String action = intent.getAction();
if (ACTION_NEW_RAW_CONTACT.equals(action)) {
createRawContact(intent);
+ } else if (ACTION_SAVE_CONTACT.equals(action)) {
+ saveContact(intent);
} else if (ACTION_CREATE_GROUP.equals(action)) {
createGroup(intent);
} else if (ACTION_RENAME_GROUP.equals(action)) {
@@ -199,6 +218,121 @@
}
/**
+ * Creates an intent that can be sent to this service to create a new raw contact
+ * using data presented as a set of ContentValues.
+ */
+ public static Intent createSaveContactIntent(Context context, EntityDeltaList state,
+ String saveModeExtraKey, int saveMode, Class<?> callbackActivity,
+ String callbackAction) {
+ Intent serviceIntent = new Intent(
+ context, ContactSaveService.class);
+ serviceIntent.setAction(ContactSaveService.ACTION_SAVE_CONTACT);
+ serviceIntent.putExtra(EXTRA_CONTACT_STATE, (Parcelable) state);
+
+ // Callback intent will be invoked by the service once the contact is
+ // saved. The service will put the URI of the new contact as "data" on
+ // the callback intent.
+ Intent callbackIntent = new Intent(context, callbackActivity);
+ callbackIntent.putExtra(saveModeExtraKey, saveMode);
+ callbackIntent.setAction(callbackAction);
+ callbackIntent.setFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_CALLBACK_INTENT, callbackIntent);
+ return serviceIntent;
+ }
+
+ private void saveContact(Intent intent) {
+ EntityDeltaList state = intent.getParcelableExtra(EXTRA_CONTACT_STATE);
+ Intent callbackIntent = intent.getParcelableExtra(EXTRA_CALLBACK_INTENT);
+
+ // Trim any empty fields, and RawContacts, before persisting
+ final AccountTypeManager accountTypes = AccountTypeManager.getInstance(this);
+ EntityModifier.trimEmpty(state, accountTypes);
+
+ Uri lookupUri = null;
+
+ final ContentResolver resolver = getContentResolver();
+
+ // Attempt to persist changes
+ int tries = 0;
+ while (tries++ < PERSIST_TRIES) {
+ try {
+ // Build operations and try applying
+ final ArrayList<ContentProviderOperation> diff = state.buildDiff();
+ ContentProviderResult[] results = null;
+ if (!diff.isEmpty()) {
+ results = resolver.applyBatch(ContactsContract.AUTHORITY, diff);
+ }
+
+ final long rawContactId = getRawContactId(state, diff, results);
+ if (rawContactId == -1) {
+ throw new IllegalStateException("Could not determine RawContact ID after save");
+ }
+ final Uri rawContactUri = ContentUris.withAppendedId(
+ RawContacts.CONTENT_URI, rawContactId);
+ lookupUri = RawContacts.getContactLookupUri(resolver, rawContactUri);
+ Log.v(TAG, "Saved contact. New URI: " + lookupUri);
+ break;
+
+ } catch (RemoteException e) {
+ // Something went wrong, bail without success
+ Log.e(TAG, "Problem persisting user edits", e);
+ break;
+
+ } catch (OperationApplicationException e) {
+ // Version consistency failed, re-parent change and try again
+ Log.w(TAG, "Version consistency failed, re-parenting: " + e.toString());
+ final StringBuilder sb = new StringBuilder(RawContacts._ID + " IN(");
+ boolean first = true;
+ final int count = state.size();
+ for (int i = 0; i < count; i++) {
+ Long rawContactId = state.getRawContactId(i);
+ if (rawContactId != null && rawContactId != -1) {
+ if (!first) {
+ sb.append(',');
+ }
+ sb.append(rawContactId);
+ first = false;
+ }
+ }
+ sb.append(")");
+
+ if (first) {
+ throw new IllegalStateException("Version consistency failed for a new contact");
+ }
+
+ final EntityDeltaList newState = EntityDeltaList.fromQuery(resolver,
+ sb.toString(), null, null);
+ state = EntityDeltaList.mergeAfter(newState, state);
+ }
+ }
+
+ callbackIntent.setData(lookupUri);
+
+ startActivity(callbackIntent);
+ }
+
+ private long getRawContactId(EntityDeltaList state,
+ final ArrayList<ContentProviderOperation> diff,
+ final ContentProviderResult[] results) {
+ long rawContactId = state.findRawContactId();
+ if (rawContactId != -1) {
+ return rawContactId;
+ }
+
+ final int diffSize = diff.size();
+ for (int i = 0; i < diffSize; i++) {
+ ContentProviderOperation operation = diff.get(i);
+ if (operation.getType() == ContentProviderOperation.TYPE_INSERT
+ && operation.getUri().getEncodedPath().contains(
+ RawContacts.CONTENT_URI.getEncodedPath())) {
+ return ContentUris.parseId(results[i].uri);
+ }
+ }
+ return -1;
+ }
+
+ /**
* Creates an intent that can be sent to this service to create a new group.
*/
public static Intent createNewGroupIntent(Context context, Account account, String label,
diff --git a/src/com/android/contacts/activities/ContactEditorActivity.java b/src/com/android/contacts/activities/ContactEditorActivity.java
index 00e0ab0..3452edc 100644
--- a/src/com/android/contacts/activities/ContactEditorActivity.java
+++ b/src/com/android/contacts/activities/ContactEditorActivity.java
@@ -50,6 +50,7 @@
private static final String TAG = "ContactEditorActivity";
public static final String ACTION_JOIN_COMPLETED = "joinCompleted";
+ public static final String ACTION_SAVE_COMPLETED = "saveCompleted";
private ContactEditorFragment mFragment;
private Button mDoneButton;
@@ -113,6 +114,10 @@
String action = intent.getAction();
if (Intent.ACTION_EDIT.equals(action)) {
mFragment.setIntentExtras(intent.getExtras());
+ } else if (ACTION_SAVE_COMPLETED.equals(action)) {
+ mFragment.onSaveCompleted(true,
+ intent.getIntExtra(ContactEditorFragment.SAVE_MODE_EXTRA_KEY, SaveMode.CLOSE),
+ intent.getData());
} else if (ACTION_JOIN_COMPLETED.equals(action)) {
mFragment.onJoinCompleted(intent.getData());
}
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index b117d77..f8415d2 100644
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -31,8 +31,6 @@
import com.android.contacts.model.EntityDeltaList;
import com.android.contacts.model.EntityModifier;
import com.android.contacts.model.GoogleAccountType;
-import com.android.contacts.util.EmptyService;
-import com.android.contacts.util.WeakAsyncTask;
import android.accounts.Account;
import android.app.Activity;
@@ -43,9 +41,6 @@
import android.app.LoaderManager;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.ActivityNotFoundException;
-import android.content.ContentProviderOperation;
-import android.content.ContentProviderResult;
-import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
@@ -54,7 +49,6 @@
import android.content.Entity;
import android.content.Intent;
import android.content.Loader;
-import android.content.OperationApplicationException;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Rect;
@@ -62,7 +56,6 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
-import android.os.RemoteException;
import android.os.SystemClock;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Email;
@@ -115,11 +108,12 @@
private static final String KEY_RAW_CONTACT_ID_REQUESTING_PHOTO = "photorequester";
private static final String KEY_VIEW_ID_GENERATOR = "viewidgenerator";
private static final String KEY_CURRENT_PHOTO_FILE = "currentphotofile";
- private static final String KEY_QUERY_SELECTION = "queryselection";
private static final String KEY_CONTACT_ID_FOR_JOIN = "contactidforjoin";
private static final String KEY_SHOW_JOIN_SUGGESTIONS = "showJoinSuggestions";
private static final String KEY_ENABLED = "enabled";
+ public static final String SAVE_MODE_EXTRA_KEY = "saveMode";
+
/**
* An intent extra that forces the editor to add the edited contact
* to the default group (e.g. "My Contacts").
@@ -215,8 +209,6 @@
private Bundle mIntentExtras;
private Listener mListener;
- private String mQuerySelection;
-
private long mContactIdForJoin;
private LinearLayout mContent;
@@ -316,6 +308,8 @@
// Load Accounts async so that we can present them
selectAccountAndCreateContact();
}
+ } else if (ContactEditorActivity.ACTION_SAVE_COMPLETED.equals(mAction)) {
+ // do nothing
} else throw new IllegalArgumentException("Unknown Action String " + mAction +
". Only support " + Intent.ACTION_EDIT + " or " + Intent.ACTION_INSERT);
}
@@ -364,7 +358,6 @@
if (fileName != null) {
mCurrentPhotoFile = new File(fileName);
}
- mQuerySelection = savedState.getString(KEY_QUERY_SELECTION);
mContactIdForJoin = savedState.getLong(KEY_CONTACT_ID_FOR_JOIN);
mAggregationSuggestionsRawContactId = savedState.getLong(KEY_SHOW_JOIN_SUGGESTIONS);
mEnabled = savedState.getBoolean(KEY_ENABLED);
@@ -409,19 +402,7 @@
private void bindEditorsForExistingContact(ContactLoader.Result data) {
setEnabled(true);
- // Build Filter mQuerySelection
- final ArrayList<Entity> entities = data.getEntities();
- final StringBuilder sb = new StringBuilder(RawContacts._ID + " IN(");
- final int count = entities.size();
- for (int i = 0; i < count; i++) {
- if (i > 0) {
- sb.append(',');
- }
- sb.append(entities.get(i).getEntityValues().get(RawContacts._ID));
- }
- sb.append(")");
- mQuerySelection = sb.toString();
- mState = EntityDeltaList.fromIterator(entities.iterator());
+ mState = EntityDeltaList.fromIterator(data.getEntities().iterator());
setIntentExtras(mIntentExtras);
mIntentExtras = null;
@@ -671,14 +652,12 @@
// If we just started creating a new contact and haven't added any data, it's too
// early to do a join
- if (mState.size() == 1 && mState.get(0).isContactInsert()) {
- final AccountTypeManager accountTypes = AccountTypeManager.getInstance(mContext);
- EntityModifier.trimEmpty(mState, accountTypes);
- if (mState.buildDiff().isEmpty()) {
- Toast.makeText(getActivity(), R.string.toast_join_with_empty_contact,
- Toast.LENGTH_LONG).show();
- return true;
- }
+ final AccountTypeManager accountTypes = AccountTypeManager.getInstance(mContext);
+ if (mState.size() == 1 && mState.get(0).isContactInsert()
+ && !EntityModifier.hasChanges(mState, accountTypes)) {
+ Toast.makeText(getActivity(), R.string.toast_join_with_empty_contact,
+ Toast.LENGTH_LONG).show();
+ return true;
}
return save(SaveMode.JOIN);
@@ -772,24 +751,24 @@
}
// If we are about to close the editor - there is no need to refresh the data
- if (saveMode == SaveMode.CLOSE) {
+ if (saveMode == SaveMode.CLOSE || saveMode == SaveMode.SPLIT) {
getLoaderManager().destroyLoader(LOADER_DATA);
}
mStatus = Status.SAVING;
- // Trim any empty fields, and RawContacts, before persisting
final AccountTypeManager accountTypes = AccountTypeManager.getInstance(mContext);
- EntityModifier.trimEmpty(mState, accountTypes);
-
- if (mState.buildDiff().isEmpty()) {
- onSaveCompleted(true, saveMode, mLookupUri);
+ if (!EntityModifier.hasChanges(mState, accountTypes)) {
+ onSaveCompleted(false, saveMode, mLookupUri);
return true;
}
- final PersistTask task = new PersistTask(this, saveMode);
- task.execute(mState);
+ setEnabled(false);
+ Intent intent = ContactSaveService.createSaveContactIntent(getActivity(), mState,
+ SAVE_MODE_EXTRA_KEY, saveMode, getActivity().getClass(),
+ ContactEditorActivity.ACTION_SAVE_COMPLETED);
+ getActivity().startService(intent);
return true;
}
@@ -837,11 +816,21 @@
}
public void onJoinCompleted(Uri uri) {
- onSaveCompleted(uri != null, SaveMode.RELOAD, uri);
+ onSaveCompleted(false, SaveMode.RELOAD, uri);
}
- public void onSaveCompleted(boolean success, int saveMode, Uri contactLookupUri) {
- Log.d(TAG, "onSaveCompleted(" + success + ", " + saveMode + ", " + contactLookupUri);
+ public void onSaveCompleted(boolean hadChanges, int saveMode, Uri contactLookupUri) {
+ boolean success = contactLookupUri != null;
+ Log.d(TAG, "onSaveCompleted(" + saveMode + ", " + contactLookupUri);
+ if (hadChanges) {
+ if (success) {
+ if (saveMode != SaveMode.JOIN) {
+ Toast.makeText(mContext, R.string.contactSavedToast, Toast.LENGTH_SHORT).show();
+ }
+ } else {
+ Toast.makeText(mContext, R.string.contactSavedErrorToast, Toast.LENGTH_LONG).show();
+ }
+ }
switch (saveMode) {
case SaveMode.CLOSE:
case SaveMode.HOME:
@@ -877,6 +866,7 @@
if (mListener != null) mListener.onSaveFinished(resultCode, resultIntent,
saveMode == SaveMode.HOME);
break;
+
case SaveMode.RELOAD:
case SaveMode.JOIN:
if (success && contactLookupUri != null) {
@@ -893,14 +883,14 @@
}
}
break;
+
case SaveMode.SPLIT:
- setEnabled(true);
+ mStatus = Status.CLOSING;
if (mListener != null) {
mListener.onContactSplit(contactLookupUri);
} else {
Log.d(TAG, "No listener registered, can not call onSplitFinished");
}
- mStatus = Status.EDITING;
break;
}
}
@@ -1430,133 +1420,6 @@
return rect;
}
- // TODO: There has to be a nicer way than this WeakAsyncTask...? Maybe call a service?
- /**
- * Background task for persisting edited contact data, using the changes
- * defined by a set of {@link EntityDelta}. This task starts
- * {@link EmptyService} to make sure the background thread can finish
- * persisting in cases where the system wants to reclaim our process.
- */
- public static class PersistTask extends
- 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 final Context mContext;
-
- private int mSaveMode;
- private Uri mContactLookupUri = null;
-
- public PersistTask(ContactEditorFragment target, int saveMode) {
- super(target);
- mSaveMode = saveMode;
- mContext = target.mContext;
- }
-
- /** {@inheritDoc} */
- @Override
- protected void onPreExecute(ContactEditorFragment target) {
- target.setEnabled(false);
-
- // 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));
- }
-
- /** {@inheritDoc} */
- @Override
- protected Integer doInBackground(ContactEditorFragment target, EntityDeltaList... params) {
- final ContentResolver resolver = mContext.getContentResolver();
-
- EntityDeltaList state = params[0];
-
- // Attempt to persist changes
- int tries = 0;
- Integer result = RESULT_FAILURE;
- while (tries++ < PERSIST_TRIES) {
- try {
- // Build operations and try applying
- final ArrayList<ContentProviderOperation> diff = state.buildDiff();
- ContentProviderResult[] results = null;
- if (!diff.isEmpty()) {
- results = resolver.applyBatch(ContactsContract.AUTHORITY, diff);
- }
-
- final long rawContactId = getRawContactId(state, diff, results);
- if (rawContactId != -1) {
- final Uri rawContactUri = ContentUris.withAppendedId(
- RawContacts.CONTENT_URI, rawContactId);
-
- // convert the raw contact URI to a contact URI
- mContactLookupUri = RawContacts.getContactLookupUri(resolver,
- rawContactUri);
- Log.d(TAG, "Looked up RawContact Uri " + rawContactUri +
- " into ContactLookupUri " + mContactLookupUri);
- } else {
- Log.w(TAG, "Could not determine RawContact ID after save");
- }
- result = (diff.size() > 0) ? RESULT_SUCCESS : RESULT_UNCHANGED;
- break;
-
- } catch (RemoteException e) {
- // Something went wrong, bail without success
- Log.e(TAG, "Problem persisting user edits", e);
- break;
-
- } catch (OperationApplicationException e) {
- // Version consistency failed, re-parent change and try again
- Log.w(TAG, "Version consistency failed, re-parenting: " + e.toString());
- final EntityDeltaList newState = EntityDeltaList.fromQuery(resolver,
- target.mQuerySelection, null, null);
- state = EntityDeltaList.mergeAfter(newState, state);
- }
- }
-
- return result;
- }
-
- private long getRawContactId(EntityDeltaList state,
- final ArrayList<ContentProviderOperation> diff,
- final ContentProviderResult[] results) {
- long rawContactId = state.findRawContactId();
- if (rawContactId != -1) {
- return rawContactId;
- }
-
-
- // we gotta do some searching for the id
- final int diffSize = diff.size();
- for (int i = 0; i < diffSize; i++) {
- ContentProviderOperation operation = diff.get(i);
- if (operation.getType() == ContentProviderOperation.TYPE_INSERT
- && operation.getUri().getEncodedPath().contains(
- RawContacts.CONTENT_URI.getEncodedPath())) {
- return ContentUris.parseId(results[i].uri);
- }
- }
- return -1;
- }
-
- /** {@inheritDoc} */
- @Override
- protected void onPostExecute(ContactEditorFragment target, Integer result) {
- Log.d(TAG, "onPostExecute(something," + result + "). mSaveMode=" + mSaveMode);
- if (result == RESULT_SUCCESS && mSaveMode != SaveMode.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();
- }
-
- // Stop the service that was protecting us
- mContext.stopService(new Intent(mContext, EmptyService.class));
-
- target.onSaveCompleted(result != RESULT_FAILURE, mSaveMode, mContactLookupUri);
- }
- }
-
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putParcelable(KEY_URI, mLookupUri);
@@ -1572,7 +1435,6 @@
if (mCurrentPhotoFile != null) {
outState.putString(KEY_CURRENT_PHOTO_FILE, mCurrentPhotoFile.toString());
}
- outState.putString(KEY_QUERY_SELECTION, mQuerySelection);
outState.putLong(KEY_CONTACT_ID_FOR_JOIN, mContactIdForJoin);
outState.putLong(KEY_SHOW_JOIN_SUGGESTIONS, mAggregationSuggestionsRawContactId);
outState.putBoolean(KEY_ENABLED, mEnabled);
diff --git a/src/com/android/contacts/model/EntityDeltaList.java b/src/com/android/contacts/model/EntityDeltaList.java
index a998a37..9b7bdc6 100644
--- a/src/com/android/contacts/model/EntityDeltaList.java
+++ b/src/com/android/contacts/model/EntityDeltaList.java
@@ -340,10 +340,18 @@
mSplitRawContacts = true;
}
+ public boolean isMarkedForSplitting() {
+ return mSplitRawContacts;
+ }
+
public void setJoinWithRawContacts(long[] rawContactIds) {
mJoinWithRawContactIds = rawContactIds;
}
+ public boolean isMarkedForJoining() {
+ return mJoinWithRawContactIds != null && mJoinWithRawContactIds.length > 0;
+ }
+
/** {@inheritDoc} */
public int describeContents() {
// Nothing special about this parcel
@@ -358,6 +366,7 @@
dest.writeParcelable(delta, flags);
}
dest.writeLongArray(mJoinWithRawContactIds);
+ dest.writeInt(mSplitRawContacts ? 1 : 0);
}
@SuppressWarnings("unchecked")
@@ -368,6 +377,7 @@
this.add(source.<EntityDelta> readParcelable(loader));
}
mJoinWithRawContactIds = source.createLongArray();
+ mSplitRawContacts = source.readInt() != 0;
}
public static final Parcelable.Creator<EntityDeltaList> CREATOR =
diff --git a/src/com/android/contacts/model/EntityModifier.java b/src/com/android/contacts/model/EntityModifier.java
index c58d813..4d4e7a8 100644
--- a/src/com/android/contacts/model/EntityModifier.java
+++ b/src/com/android/contacts/model/EntityModifier.java
@@ -344,7 +344,7 @@
}
final ValuesDelta child = ValuesDelta.fromAfter(after);
- state.addEntry(child);
+ state.addEntry(child);
return child;
}
@@ -357,11 +357,26 @@
public static void trimEmpty(EntityDeltaList set, AccountTypeManager accountTypes) {
for (EntityDelta state : set) {
final String accountType = state.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
- final AccountType source = accountTypes.getAccountType(accountType);
- trimEmpty(state, source);
+ final AccountType type = accountTypes.getAccountType(accountType);
+ trimEmpty(state, type);
}
}
+ public static boolean hasChanges(EntityDeltaList set, AccountTypeManager accountTypes) {
+ if (set.isMarkedForSplitting() || set.isMarkedForJoining()) {
+ return true;
+ }
+
+ for (EntityDelta state : set) {
+ final String accountType = state.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
+ final AccountType type = accountTypes.getAccountType(accountType);
+ if (hasChanges(state, type)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Processing to trim any empty {@link ValuesDelta} rows from the given
* {@link EntityDelta}, assuming the given {@link AccountType} dictates
@@ -406,6 +421,21 @@
}
}
+ private static boolean hasChanges(EntityDelta state, AccountType accountType) {
+ for (DataKind kind : accountType.getSortedDataKinds()) {
+ final String mimeType = kind.mimeType;
+ final ArrayList<ValuesDelta> entries = state.getMimeEntries(mimeType);
+ if (entries == null) continue;
+
+ for (ValuesDelta entry : entries) {
+ if ((entry.isInsert() || entry.isUpdate()) && !isEmpty(entry, kind)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
/**
* Test if the given {@link ValuesDelta} would be considered "empty" in
* terms of {@link DataKind#fieldList}.