summaryrefslogtreecommitdiffstats
path: root/emailcommon
diff options
context:
space:
mode:
authorYu Ping Hu <yph@google.com>2013-09-12 15:23:54 -0700
committerYu Ping Hu <yph@google.com>2013-09-15 15:43:35 -0700
commite9849404652f317c4fb3af822fd6fa9cd23ad881 (patch)
tree9a73b67e5e792a40414d7d6c29345211c47ab24b /emailcommon
parented610f749c317dd6bc9c970fa56fb8c8cf585842 (diff)
downloadandroid_packages_apps_Email-e9849404652f317c4fb3af822fd6fa9cd23ad881.tar.gz
android_packages_apps_Email-e9849404652f317c4fb3af822fd6fa9cd23ad881.tar.bz2
android_packages_apps_Email-e9849404652f317c4fb3af822fd6fa9cd23ad881.zip
Fix MessageStateChange to include mailbox id.
Also ignore messages without server ids for moves and state changes. Also cleanup to match needs of EAS upsync. Bug: 10678136 Change-Id: Id4d5229b8479e61bd718b707b0d2bc77a9e68046
Diffstat (limited to 'emailcommon')
-rwxr-xr-xemailcommon/src/com/android/emailcommon/provider/EmailContent.java2
-rw-r--r--emailcommon/src/com/android/emailcommon/provider/Mailbox.java12
-rw-r--r--emailcommon/src/com/android/emailcommon/provider/MessageChangeLogTable.java1
-rw-r--r--emailcommon/src/com/android/emailcommon/provider/MessageMove.java62
-rw-r--r--emailcommon/src/com/android/emailcommon/provider/MessageStateChange.java103
5 files changed, 169 insertions, 11 deletions
diff --git a/emailcommon/src/com/android/emailcommon/provider/EmailContent.java b/emailcommon/src/com/android/emailcommon/provider/EmailContent.java
index 1f1aa508f..5655d37e7 100755
--- a/emailcommon/src/com/android/emailcommon/provider/EmailContent.java
+++ b/emailcommon/src/com/android/emailcommon/provider/EmailContent.java
@@ -687,6 +687,8 @@ public abstract class EmailContent {
public static final String ACCOUNT_KEY_SELECTION =
MessageColumns.ACCOUNT_KEY + "=?";
+ public static final String[] MAILBOX_KEY_PROJECTION = new String[] { MAILBOX_KEY };
+
/**
* Selection for messages that are loaded
*
diff --git a/emailcommon/src/com/android/emailcommon/provider/Mailbox.java b/emailcommon/src/com/android/emailcommon/provider/Mailbox.java
index c4c8a96dd..186b98872 100644
--- a/emailcommon/src/com/android/emailcommon/provider/Mailbox.java
+++ b/emailcommon/src/com/android/emailcommon/provider/Mailbox.java
@@ -141,6 +141,18 @@ public class Mailbox extends EmailContent implements MailboxColumns, Parcelable
private static final String[] ACCOUNT_KEY_PROJECTION = { MailboxColumns.ACCOUNT_KEY };
private static final int ACCOUNT_KEY_PROJECTION_ACCOUNT_KEY_COLUMN = 0;
+ /**
+ * Projection for querying data needed during a sync.
+ */
+ public interface ProjectionSyncData {
+ public static final int COLUMN_SERVER_ID = 0;
+ public static final int COLUMN_SYNC_KEY = 1;
+
+ public static final String[] PROJECTION = {
+ MailboxColumns.SERVER_ID, MailboxColumns.SYNC_KEY
+ };
+ };
+
public static final long NO_MAILBOX = -1;
// Sentinel values for the mSyncInterval field of both Mailbox records
diff --git a/emailcommon/src/com/android/emailcommon/provider/MessageChangeLogTable.java b/emailcommon/src/com/android/emailcommon/provider/MessageChangeLogTable.java
index 9833d3217..fb113b2eb 100644
--- a/emailcommon/src/com/android/emailcommon/provider/MessageChangeLogTable.java
+++ b/emailcommon/src/com/android/emailcommon/provider/MessageChangeLogTable.java
@@ -37,6 +37,7 @@ public abstract class MessageChangeLogTable {
public static final String STATUS_PROCESSING_STRING = String.valueOf(STATUS_PROCESSING);
/** Status value indicating this move failed to upsync. */
public static final int STATUS_FAILED = 2;
+ public static final String STATUS_FAILED_STRING = String.valueOf(STATUS_FAILED);
/** Selection string for querying this table. */
private static final String SELECTION_BY_ACCOUNT_KEY_AND_STATUS =
diff --git a/emailcommon/src/com/android/emailcommon/provider/MessageMove.java b/emailcommon/src/com/android/emailcommon/provider/MessageMove.java
index 1afecbfdf..9d0cae16a 100644
--- a/emailcommon/src/com/android/emailcommon/provider/MessageMove.java
+++ b/emailcommon/src/com/android/emailcommon/provider/MessageMove.java
@@ -1,6 +1,7 @@
package com.android.emailcommon.provider;
import android.content.ContentResolver;
+import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
@@ -37,10 +38,14 @@ public class MessageMove extends MessageChangeLogTable {
/** Column name for the server-side id for dstFolderKey. */
public static final String DST_FOLDER_SERVER_ID = "dstFolderServerId";
+ /** Selection to get the last synced folder for a message. */
+ private static final String SELECTION_LAST_SYNCED_MAILBOX = MESSAGE_KEY + "=? and " + STATUS
+ + "!=" + STATUS_FAILED_STRING;
+
/**
* Projection for a query to get all columns necessary for an actual move.
*/
- private static final class ProjectionMoveQuery {
+ private interface ProjectionMoveQuery {
public static final int COLUMN_ID = 0;
public static final int COLUMN_MESSAGE_KEY = 1;
public static final int COLUMN_SERVER_ID = 2;
@@ -56,6 +61,16 @@ public class MessageMove extends MessageChangeLogTable {
};
}
+ /**
+ * Projection for a query to get the original folder id for a message.
+ */
+ private interface ProjectionLastSyncedMailboxQuery {
+ public static final int COLUMN_ID = 0;
+ public static final int COLUMN_SRC_FOLDER_KEY = 1;
+
+ public static final String[] PROJECTION = new String[] { ID, SRC_FOLDER_KEY };
+ }
+
// The actual fields.
private final long mSrcFolderKey;
private long mDstFolderKey;
@@ -151,7 +166,9 @@ public class MessageMove extends MessageChangeLogTable {
final ArrayList<MessageMove> moves = new ArrayList(moveCount);
for (int i = 0; i < movesMap.size(); ++i) {
final MessageMove move = movesMap.valueAt(i);
- if (move.mSrcFolderKey == move.mDstFolderKey) {
+ // We also treat changes without a server id as a no-op.
+ if ((move.mServerId == null || move.mServerId.length() == 0) ||
+ move.mSrcFolderKey == move.mDstFolderKey) {
unmovedMessages[unmovedMessagesCount] = move.mMessageKey;
++unmovedMessagesCount;
} else {
@@ -199,4 +216,45 @@ public class MessageMove extends MessageChangeLogTable {
final int count) {
failMessages(cr, CONTENT_URI, messageKeys, count);
}
+
+ /**
+ * Get the id for the mailbox this message is in (from the server's point of view).
+ * @param cr A {@link ContentResolver}.
+ * @param messageId The message we're interested in.
+ * @return The id for the mailbox this message was in.
+ */
+ public static long getLastSyncedMailboxForMessage(final ContentResolver cr,
+ final long messageId) {
+ // Check if there's a pending move and get the original mailbox id.
+ final String[] selectionArgs = { String.valueOf(messageId) };
+ final Cursor moveCursor = cr.query(CONTENT_URI, ProjectionLastSyncedMailboxQuery.PROJECTION,
+ SELECTION_LAST_SYNCED_MAILBOX, selectionArgs, ID + " ASC");
+ if (moveCursor != null) {
+ try {
+ if (moveCursor.moveToFirst()) {
+ // We actually only care about the oldest one, i.e. the one we last got
+ // from the server before we started mucking with it.
+ return moveCursor.getLong(
+ ProjectionLastSyncedMailboxQuery.COLUMN_SRC_FOLDER_KEY);
+ }
+ } finally {
+ moveCursor.close();
+ }
+ }
+
+ // There are no pending moves for this message, so use the one in the Message table.
+ final Cursor messageCursor = cr.query(ContentUris.withAppendedId(
+ EmailContent.Message.CONTENT_URI, messageId),
+ EmailContent.Message.MAILBOX_KEY_PROJECTION, null, null, null);
+ if (messageCursor != null) {
+ try {
+ if (messageCursor.moveToFirst()) {
+ return messageCursor.getLong(0);
+ }
+ } finally {
+ messageCursor.close();
+ }
+ }
+ return Mailbox.NO_MAILBOX;
+ }
}
diff --git a/emailcommon/src/com/android/emailcommon/provider/MessageStateChange.java b/emailcommon/src/com/android/emailcommon/provider/MessageStateChange.java
index dc0d294be..78b84cc45 100644
--- a/emailcommon/src/com/android/emailcommon/provider/MessageStateChange.java
+++ b/emailcommon/src/com/android/emailcommon/provider/MessageStateChange.java
@@ -43,7 +43,7 @@ public class MessageStateChange extends MessageChangeLogTable {
/**
* Projection for a query to get all columns necessary for an actual change.
*/
- private static final class ProjectionChangeQuery {
+ private interface ProjectionChangeQuery {
public static final int COLUMN_ID = 0;
public static final int COLUMN_MESSAGE_KEY = 1;
public static final int COLUMN_SERVER_ID = 2;
@@ -64,15 +64,32 @@ public class MessageStateChange extends MessageChangeLogTable {
private int mNewFlagRead;
private final int mOldFlagFavorite;
private int mNewFlagFavorite;
+ private final long mMailboxId;
private MessageStateChange(final long messageKey,final String serverId, final long id,
final int oldFlagRead, final int newFlagRead,
- final int oldFlagFavorite, final int newFlagFavorite) {
+ final int oldFlagFavorite, final int newFlagFavorite,
+ final long mailboxId) {
super(messageKey, serverId, id);
mOldFlagRead = oldFlagRead;
mNewFlagRead = newFlagRead;
mOldFlagFavorite = oldFlagFavorite;
mNewFlagFavorite = newFlagFavorite;
+ mMailboxId = mailboxId;
+ }
+
+ public final int getNewFlagRead() {
+ if (mOldFlagRead == mNewFlagRead) {
+ return VALUE_UNCHANGED;
+ }
+ return mNewFlagRead;
+ }
+
+ public final int getNewFlagFavorite() {
+ if (mOldFlagFavorite == mNewFlagFavorite) {
+ return VALUE_UNCHANGED;
+ }
+ return mNewFlagFavorite;
}
/**
@@ -82,7 +99,18 @@ public class MessageStateChange extends MessageChangeLogTable {
CONTENT_URI = EmailContent.CONTENT_URI.buildUpon().appendEncodedPath(PATH).build();
}
- public static List<MessageStateChange> getChanges(final Context context, final long accountId) {
+ /**
+ * Gets final state changes to upsync to the server, setting the status in the DB for all rows
+ * to {@link #STATUS_PROCESSING} that are being updated and to {@link #STATUS_FAILED} for any
+ * old updates. Messages whose sequence of changes results in a no-op are cleared from the DB
+ * without any upsync.
+ * @param context A {@link Context}.
+ * @param accountId The account we want to update.
+ * @param ignoreFavorites Whether to ignore changes to the favorites flag.
+ * @return The final chnages to send to the server, or null if there are none.
+ */
+ public static List<MessageStateChange> getChanges(final Context context, final long accountId,
+ final boolean ignoreFavorites) {
final ContentResolver cr = context.getContentResolver();
final Cursor c = getCursor(cr, CONTENT_URI, ProjectionChangeQuery.PROJECTION, accountId);
if (c == null) {
@@ -105,8 +133,9 @@ public class MessageStateChange extends MessageChangeLogTable {
c.getInt(ProjectionChangeQuery.COLUMN_OLD_FLAG_FAVORITE);
final int newFlagFavoriteTable =
c.getInt(ProjectionChangeQuery.COLUMN_NEW_FLAG_FAVORITE);
- final int newFlagFavorite = (newFlagFavoriteTable == VALUE_UNCHANGED) ?
- oldFlagFavorite : newFlagFavoriteTable;
+ final int newFlagFavorite =
+ (ignoreFavorites || newFlagFavoriteTable == VALUE_UNCHANGED) ?
+ oldFlagFavorite : newFlagFavoriteTable;
final MessageStateChange existingChange = changesMap.get(messageKey);
if (existingChange != null) {
if (existingChange.mLastId >= id) {
@@ -120,8 +149,15 @@ public class MessageStateChange extends MessageChangeLogTable {
existingChange.mNewFlagFavorite = newFlagFavorite;
existingChange.mLastId = id;
} else {
- changesMap.put(messageKey, new MessageStateChange(messageKey, serverId, id,
- oldFlagRead, newFlagRead, oldFlagFavorite, newFlagFavorite));
+ final long mailboxId = MessageMove.getLastSyncedMailboxForMessage(cr,
+ messageKey);
+ if (mailboxId == Mailbox.NO_MAILBOX) {
+ LogUtils.e(LOG_TAG, "No mailbox id for message %d", messageKey);
+ } else {
+ changesMap.put(messageKey, new MessageStateChange(messageKey, serverId, id,
+ oldFlagRead, newFlagRead, oldFlagFavorite, newFlagFavorite,
+ mailboxId));
+ }
}
}
} finally {
@@ -136,8 +172,10 @@ public class MessageStateChange extends MessageChangeLogTable {
final ArrayList<MessageStateChange> changes = new ArrayList(count);
for (int i = 0; i < changesMap.size(); ++i) {
final MessageStateChange change = changesMap.valueAt(i);
- if (change.mOldFlagRead == change.mNewFlagRead &&
- change.mOldFlagFavorite == change.mNewFlagFavorite) {
+ // We also treat changes without a server id as a no-op.
+ if ((change.mServerId == null || change.mServerId.length() == 0) ||
+ (change.mOldFlagRead == change.mNewFlagRead &&
+ change.mOldFlagFavorite == change.mNewFlagFavorite)) {
unchangedMessages[unchangedMessagesCount] = change.mMessageKey;
++unchangedMessagesCount;
} else {
@@ -152,4 +190,51 @@ public class MessageStateChange extends MessageChangeLogTable {
}
return changes;
}
+
+ /**
+ * Rearrange the changes list to a map by mailbox id.
+ * @return The final changes to send to the server, or null if there are none.
+ */
+ public static LongSparseArray<List<MessageStateChange>> convertToChangesMap(
+ final List<MessageStateChange> changes) {
+ if (changes == null) {
+ return null;
+ }
+
+ final LongSparseArray<List<MessageStateChange>> changesMap = new LongSparseArray();
+ for (final MessageStateChange change : changes) {
+ List<MessageStateChange> list = changesMap.get(change.mMailboxId);
+ if (list == null) {
+ list = new ArrayList();
+ changesMap.put(change.mMailboxId, list);
+ }
+ list.add(change);
+ }
+ if (changesMap.size() == 0) {
+ return null;
+ }
+ return changesMap;
+ }
+
+ /**
+ * Clean up the table to reflect a successful set of upsyncs.
+ * @param cr A {@link ContentResolver}
+ * @param messageKeys The messages to update.
+ * @param count The number of messages.
+ */
+ public static void upsyncSuccessful(final ContentResolver cr, final long[] messageKeys,
+ final int count) {
+ deleteRowsForMessages(cr, CONTENT_URI, messageKeys, count);
+ }
+
+ /**
+ * Clean up the table to reflect upsyncs that need to be retried.
+ * @param cr A {@link ContentResolver}
+ * @param messageKeys The messages to update.
+ * @param count The number of messages.
+ */
+ public static void upsyncRetry(final ContentResolver cr, final long[] messageKeys,
+ final int count) {
+ retryMessages(cr, CONTENT_URI, messageKeys, count);
+ }
}