diff options
| author | Yu Ping Hu <yph@google.com> | 2013-09-12 15:23:54 -0700 |
|---|---|---|
| committer | Yu Ping Hu <yph@google.com> | 2013-09-15 15:43:35 -0700 |
| commit | e9849404652f317c4fb3af822fd6fa9cd23ad881 (patch) | |
| tree | 9a73b67e5e792a40414d7d6c29345211c47ab24b /emailcommon | |
| parent | ed610f749c317dd6bc9c970fa56fb8c8cf585842 (diff) | |
| download | android_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')
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); + } } |
