summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYu Ping Hu <yph@google.com>2013-08-23 09:39:58 -0700
committerYu Ping Hu <yph@google.com>2013-09-09 15:47:13 -0700
commit6f0cf29be849d3a95a9168cb6fa09de12635e818 (patch)
tree2b67a3c12de306884fca7758f2986f626c029283
parent456e7eab30979211346d3ede47bd304701ef9c2a (diff)
downloadExchange-6f0cf29be849d3a95a9168cb6fa09de12635e818.tar.gz
Add MoveItems operation.
Change-Id: I39d7aeb2de4a01ec9237f574b752b57662829eb4
-rw-r--r--Android.mk1
-rw-r--r--src/com/android/exchange/adapter/MoveItemsParser.java8
-rw-r--r--src/com/android/exchange/eas/EasMoveItems.java143
-rw-r--r--src/com/android/exchange/eas/EasOperation.java8
-rw-r--r--src/com/android/exchange/eas/EasSync.java9
-rw-r--r--src/com/android/exchange/service/EmailSyncAdapterService.java4
6 files changed, 168 insertions, 5 deletions
diff --git a/Android.mk b/Android.mk
index 0551fe8f..2cbb4788 100644
--- a/Android.mk
+++ b/Android.mk
@@ -34,6 +34,7 @@ LOCAL_SRC_FILES += $(call all-java-files-under, build/src)
LOCAL_STATIC_JAVA_LIBRARIES := android-common com.android.emailcommon com.android.emailsync
LOCAL_STATIC_JAVA_LIBRARIES += calendar-common
+LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4
LOCAL_PACKAGE_NAME := Exchange2
LOCAL_OVERRIDES_PACKAGES := Exchange
diff --git a/src/com/android/exchange/adapter/MoveItemsParser.java b/src/com/android/exchange/adapter/MoveItemsParser.java
index a654c76c..be11727f 100644
--- a/src/com/android/exchange/adapter/MoveItemsParser.java
+++ b/src/com/android/exchange/adapter/MoveItemsParser.java
@@ -27,6 +27,7 @@ public class MoveItemsParser extends Parser {
private static final String TAG = "MoveItemsParser";
private int mStatusCode = 0;
private String mNewServerId;
+ private String mSourceServerId;
// These are the EAS status codes for MoveItems
private static final int STATUS_NO_SOURCE_FOLDER = 1;
@@ -54,6 +55,10 @@ public class MoveItemsParser extends Parser {
return mNewServerId;
}
+ public String getSourceServerId() {
+ return mSourceServerId;
+ }
+
public void parseResponse() throws IOException {
while (nextTag(Tags.MOVE_RESPONSE) != END) {
if (tag == Tags.MOVE_STATUS) {
@@ -87,6 +92,9 @@ public class MoveItemsParser extends Parser {
} else if (tag == Tags.MOVE_DSTMSGID) {
mNewServerId = getValue();
LogUtils.i(TAG, "Moved message id is now: %s", mNewServerId);
+ } else if (tag == Tags.MOVE_SRCMSGID) {
+ mSourceServerId = getValue();
+ LogUtils.i(TAG, "Source message id is: %s", mNewServerId);
} else {
skipTag();
}
diff --git a/src/com/android/exchange/eas/EasMoveItems.java b/src/com/android/exchange/eas/EasMoveItems.java
new file mode 100644
index 00000000..a9c5c3fc
--- /dev/null
+++ b/src/com/android/exchange/eas/EasMoveItems.java
@@ -0,0 +1,143 @@
+package com.android.exchange.eas;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.SyncResult;
+
+import com.android.emailcommon.provider.Account;
+import com.android.emailcommon.provider.EmailContent;
+import com.android.emailcommon.provider.MessageMove;
+import com.android.exchange.EasResponse;
+import com.android.exchange.adapter.MoveItemsParser;
+import com.android.exchange.adapter.Serializer;
+import com.android.exchange.adapter.Tags;
+import com.android.mail.utils.LogUtils;
+
+import org.apache.http.HttpEntity;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Performs a MoveItems request, which is used to move items between collections.
+ * See http://msdn.microsoft.com/en-us/library/ee160102(v=exchg.80).aspx for more details.
+ * TODO: Investigate how this interacts with ItemOperations.
+ */
+public class EasMoveItems extends EasOperation {
+
+ /** Result code indicating that no moved messages were found for this account. */
+ public final static int RESULT_NO_MESSAGES = 0;
+ public final static int RESULT_OK = 1;
+
+ private static class MoveResponse {
+ public final String sourceMessageId;
+ public final String newMessageId;
+ public final int moveStatus;
+
+ public MoveResponse(final String srcMsgId, final String dstMsgId, final int status) {
+ sourceMessageId = srcMsgId;
+ newMessageId = dstMsgId;
+ moveStatus = status;
+ }
+ }
+
+ private MessageMove mMove;
+ private MoveResponse mResponse;
+
+ public EasMoveItems(final Context context, final Account account) {
+ super(context, account);
+ }
+
+ // TODO: Allow multiple messages in one request. Requires parser changes.
+ public int upsyncMovedMessages(final SyncResult syncResult) {
+ final List<MessageMove> moves = MessageMove.getMoves(mContext, mAccountId);
+ if (moves == null) {
+ return RESULT_NO_MESSAGES;
+ }
+
+ final long[][] messageIds = new long[3][moves.size()];
+ final int[] counts = new int[3];
+
+ for (final MessageMove move : moves) {
+ mMove = move;
+ final int result = performOperation(syncResult);
+ final int status;
+ if (result == RESULT_OK) {
+ processResponse(mMove, mResponse);
+ status = mResponse.moveStatus;
+ } else {
+ // TODO: Perhaps not all errors should be retried?
+ status = MoveItemsParser.STATUS_CODE_RETRY;
+ }
+ final int index = status - 1;
+ messageIds[index][counts[index]] = mMove.getMessageId();
+ ++counts[index];
+ }
+
+ final ContentResolver cr = mContext.getContentResolver();
+ MessageMove.upsyncSuccessful(cr, messageIds[0], counts[0]);
+ MessageMove.upsyncFail(cr, messageIds[1], counts[1]);
+ MessageMove.upsyncRetry(cr, messageIds[2], counts[2]);
+
+ return RESULT_OK;
+ }
+
+ @Override
+ protected String getCommand() {
+ return "MoveItems";
+ }
+
+ @Override
+ protected HttpEntity getRequestEntity() throws IOException {
+ final Serializer s = new Serializer();
+ s.start(Tags.MOVE_MOVE_ITEMS);
+ s.start(Tags.MOVE_MOVE);
+ s.data(Tags.MOVE_SRCMSGID, mMove.getServerId());
+ s.data(Tags.MOVE_SRCFLDID, mMove.getSourceFolderId());
+ s.data(Tags.MOVE_DSTFLDID, mMove.getDestFolderId());
+ s.end();
+ s.end().done();
+ return makeEntity(s);
+ }
+
+ @Override
+ protected int handleResponse(final EasResponse response, final SyncResult syncResult)
+ throws IOException {
+ if (!response.isEmpty()) {
+ final MoveItemsParser parser = new MoveItemsParser(response.getInputStream());
+ parser.parse();
+ final String sourceMessageId = parser.getSourceServerId();
+ final String newMessageId = parser.getNewServerId();
+ final int status = parser.getStatusCode();
+ mResponse = new MoveResponse(sourceMessageId, newMessageId, status);
+ }
+ return RESULT_OK;
+ }
+
+ private void processResponse(final MessageMove request, final MoveResponse response) {
+ // TODO: Eventually this should use a transaction.
+ // TODO: Improve how the parser reports statuses and how we handle them here.
+ if (!response.sourceMessageId.equals(request.getServerId())) {
+ // TODO: This is bad, but I think we need to respect the response anyway.
+ LogUtils.e(LOG_TAG, "Got a response for a message we didn't request");
+ }
+
+ final ContentValues cv = new ContentValues(1);
+ if (response.moveStatus == MoveItemsParser.STATUS_CODE_REVERT) {
+ // Restore the old mailbox id
+ cv.put(EmailContent.MessageColumns.MAILBOX_KEY, request.getSourceFolderKey());
+ } else if (response.moveStatus == MoveItemsParser.STATUS_CODE_SUCCESS) {
+ if (response.newMessageId != null
+ && !response.newMessageId.equals(response.sourceMessageId)) {
+ cv.put(EmailContent.SyncColumns.SERVER_ID, response.newMessageId);
+ }
+ }
+ if (cv.size() != 0) {
+ mContext.getContentResolver().update(
+ ContentUris.withAppendedId(EmailContent.Message.CONTENT_URI,
+ request.getMessageId()), cv, null, null);
+ }
+ }
+}
diff --git a/src/com/android/exchange/eas/EasOperation.java b/src/com/android/exchange/eas/EasOperation.java
index 1485e788..df311cd5 100644
--- a/src/com/android/exchange/eas/EasOperation.java
+++ b/src/com/android/exchange/eas/EasOperation.java
@@ -95,10 +95,10 @@ public abstract class EasOperation {
/**
* The account id for this operation.
- * Currently only used for handling provisioning errors. Ideally we should minimize the creep
- * of how this gets used (i.e. don't let it get to the intertwined state of the past).
+ * NOTE: You will be tempted to add a reference to the {@link Account} here. Resist.
+ * It's too easy for that to lead to creep and stale data.
*/
- private final long mAccountId;
+ protected final long mAccountId;
private final EasServerConnection mConnection;
private EasOperation(final Context context, final long accountId,
@@ -190,7 +190,7 @@ public abstract class EasOperation {
return RESULT_REQUEST_FAILURE;
} catch (final IllegalStateException e) {
// Subclasses use ISE to signal a hard error when building the request.
- // TODO: If executeHttpUriRequest can throw an ISE, we may want to tidy this up.
+ // TODO: Switch away from ISEs.
LogUtils.e(LOG_TAG, e, "Exception while sending request");
if (syncResult != null) {
syncResult.databaseError = true;
diff --git a/src/com/android/exchange/eas/EasSync.java b/src/com/android/exchange/eas/EasSync.java
index 34dee034..03c614b6 100644
--- a/src/com/android/exchange/eas/EasSync.java
+++ b/src/com/android/exchange/eas/EasSync.java
@@ -56,7 +56,12 @@ public class EasSync extends EasOperation {
@Override
protected HttpEntity getRequestEntity() throws IOException {
- return null;
+ final Serializer s = new Serializer();
+ s.start(Tags.SYNC_SYNC);
+ s.start(Tags.SYNC_COLLECTIONS);
+ addOneCollectionToRequest(s, mMailbox);
+ s.end().end().done();
+ return makeEntity(s);
}
@@ -80,6 +85,8 @@ public class EasSync extends EasOperation {
if (getProtocolVersion() < Eas.SUPPORTED_PROTOCOL_EX2007_SP1_DOUBLE) {
s.data(Tags.SYNC_CLASS, Eas.getFolderClass(mailbox.mType));
}
+ s.data(Tags.SYNC_SYNC_KEY, getSyncKeyForMailbox(mailbox));
+ s.data(Tags.SYNC_COLLECTION_ID, mailbox.mServerId);
s.end();
}
diff --git a/src/com/android/exchange/service/EmailSyncAdapterService.java b/src/com/android/exchange/service/EmailSyncAdapterService.java
index b2a50554..b60cd522 100644
--- a/src/com/android/exchange/service/EmailSyncAdapterService.java
+++ b/src/com/android/exchange/service/EmailSyncAdapterService.java
@@ -43,6 +43,7 @@ import com.android.exchange.Eas;
import com.android.exchange.adapter.PingParser;
import com.android.exchange.adapter.Search;
import com.android.exchange.eas.EasFolderSync;
+import com.android.exchange.eas.EasMoveItems;
import com.android.exchange.eas.EasOperation;
import com.android.exchange.eas.EasPing;
import com.android.mail.providers.UIProvider.AccountCapabilities;
@@ -528,6 +529,9 @@ public class EmailSyncAdapterService extends AbstractSyncAdapterService {
// Do the bookkeeping for starting a sync, including stopping a ping if necessary.
mSyncHandlerMap.startSync(account.mId);
+ EasMoveItems move = new EasMoveItems(context, account);
+ move.upsyncMovedMessages(syncResult);
+
// TODO: Should we refresh the account here? It may have changed while waiting for any
// pings to stop. It may not matter since the things that may have been twiddled might
// not affect syncing.