summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMarc Blank <mblank@google.com>2009-12-04 12:49:28 -0800
committerMarc Blank <mblank@google.com>2009-12-16 12:01:08 -0800
commit608ca667b25e6107d20bf2fac8243a42abfad719 (patch)
tree8d0c9da5f9e2f11ca7d0a38f236ebc300297a9fc /src
parent14d3745f9556232da05c9cd319c097a3e6f791b5 (diff)
downloadandroid_packages_apps_Email-608ca667b25e6107d20bf2fac8243a42abfad719.tar.gz
android_packages_apps_Email-608ca667b25e6107d20bf2fac8243a42abfad719.tar.bz2
android_packages_apps_Email-608ca667b25e6107d20bf2fac8243a42abfad719.zip
Don't delete referenced messages from the Exchange server DO NOT MERGE
* Addresses #2287439 incompletely * The most likely reason for a reply/forward to get stuck in the Outbox is that the referenced message has been deleted from the client, with the deletion occuring BEFORE the message gets sent (currently, the two are completely independent) * This change causes deletes NOT to be sent to the server if the message to be deleted is referenced by an outgoing message Change-Id: Iad3777282385bea82276f363d6f95ba8b07cc01c
Diffstat (limited to 'src')
-rw-r--r--src/com/android/email/provider/EmailContent.java6
-rw-r--r--src/com/android/exchange/adapter/EmailSyncAdapter.java83
2 files changed, 67 insertions, 22 deletions
diff --git a/src/com/android/email/provider/EmailContent.java b/src/com/android/email/provider/EmailContent.java
index 62c7c537e..955ea0d27 100644
--- a/src/com/android/email/provider/EmailContent.java
+++ b/src/com/android/email/provider/EmailContent.java
@@ -185,7 +185,9 @@ public abstract class EmailContent {
public static final String HTML_REPLY = "htmlReply";
// Replied-to or forwarded body (in text form)
public static final String TEXT_REPLY = "textReply";
- // Message id of the source (if this is a reply/forward)
+ // A reference to a message's unique id used in reply/forward.
+ // Protocol code can be expected to use this column in determining whether a message can be
+ // deleted safely (i.e. isn't referenced by other messages)
public static final String SOURCE_MESSAGE_KEY = "sourceMessageKey";
// The text to be placed between a reply/forward response and the original message
public static final String INTRO_TEXT = "introText";
@@ -519,7 +521,7 @@ public abstract class EmailContent {
public String mBcc;
public String mReplyTo;
- // The following transient members may be used while building and manipulating messages,
+ // The following transient members may be used while building and manipulating messages,
// but they are NOT persisted directly by EmailProvider
transient public String mText;
transient public String mHtml;
diff --git a/src/com/android/exchange/adapter/EmailSyncAdapter.java b/src/com/android/exchange/adapter/EmailSyncAdapter.java
index 6a5d2a5cd..946ae4e95 100644
--- a/src/com/android/exchange/adapter/EmailSyncAdapter.java
+++ b/src/com/android/exchange/adapter/EmailSyncAdapter.java
@@ -24,6 +24,7 @@ import com.android.email.provider.EmailProvider;
import com.android.email.provider.EmailContent.Account;
import com.android.email.provider.EmailContent.AccountColumns;
import com.android.email.provider.EmailContent.Attachment;
+import com.android.email.provider.EmailContent.Body;
import com.android.email.provider.EmailContent.Mailbox;
import com.android.email.provider.EmailContent.Message;
import com.android.email.provider.EmailContent.MessageColumns;
@@ -65,8 +66,10 @@ public class EmailSyncAdapter extends AbstractSyncAdapter {
private static final String[] MESSAGE_ID_SUBJECT_PROJECTION =
new String[] { Message.RECORD_ID, MessageColumns.SUBJECT };
+ private static final String WHERE_BODY_SOURCE_MESSAGE_KEY = Body.SOURCE_MESSAGE_KEY + "=?";
- String[] bindArguments = new String[2];
+ String[] mBindArguments = new String[2];
+ String[] mBindArgument = new String[1];
ArrayList<Long> mDeletedIdList = new ArrayList<Long>();
ArrayList<Long> mUpdatedIdList = new ArrayList<Long>();
@@ -308,10 +311,10 @@ public class EmailSyncAdapter extends AbstractSyncAdapter {
}
private Cursor getServerIdCursor(String serverId, String[] projection) {
- bindArguments[0] = serverId;
- bindArguments[1] = mMailboxIdAsString;
+ mBindArguments[0] = serverId;
+ mBindArguments[1] = mMailboxIdAsString;
return mContentResolver.query(Message.CONTENT_URI, projection,
- WHERE_SERVER_ID_AND_MAILBOX_KEY, bindArguments, null);
+ WHERE_SERVER_ID_AND_MAILBOX_KEY, mBindArguments, null);
}
private void deleteParser(ArrayList<Long> deletes, int entryTag) throws IOException {
@@ -562,46 +565,86 @@ public class EmailSyncAdapter extends AbstractSyncAdapter {
return sb.toString();
}
- @Override
- public boolean sendLocalChanges(Serializer s) throws IOException {
- ContentResolver cr = mContext.getContentResolver();
-
- // Never upsync from these folders
- if (mMailbox.mType == Mailbox.TYPE_DRAFTS || mMailbox.mType == Mailbox.TYPE_OUTBOX) {
- return false;
+ /**
+ * Note that messages in the deleted database preserve the message's unique id; therefore, we
+ * can utilize this id to find references to the message. The only reference situation at this
+ * point is in the Body table; it is when sending messages via SmartForward and SmartReply
+ */
+ private boolean messageReferenced(ContentResolver cr, long id) {
+ mBindArgument[0] = Long.toString(id);
+ // See if this id is referenced in a body
+ Cursor c = cr.query(Body.CONTENT_URI, Body.ID_PROJECTION, WHERE_BODY_SOURCE_MESSAGE_KEY,
+ mBindArgument, null);
+ try {
+ return c.moveToFirst();
+ } finally {
+ c.close();
}
+ }
+
+ /*private*/ /**
+ * Serialize commands to delete items from the server; as we find items to delete, add their
+ * id's to the deletedId's array
+ *
+ * @param s the Serializer we're using to create post data
+ * @param deletedIds ids whose deletions are being sent to the server
+ * @param first whether or not this is the first command being sent
+ * @return true if SYNC_COMMANDS hasn't been sent (false otherwise)
+ * @throws IOException
+ */
+ boolean sendDeletedItems(Serializer s, ArrayList<Long> deletedIds, boolean first)
+ throws IOException {
+ ContentResolver cr = mContext.getContentResolver();
// Find any of our deleted items
Cursor c = cr.query(Message.DELETED_CONTENT_URI, Message.LIST_PROJECTION,
MessageColumns.MAILBOX_KEY + '=' + mMailbox.mId, null, null);
- boolean first = true;
// We keep track of the list of deleted item id's so that we can remove them from the
// deleted table after the server receives our command
- mDeletedIdList.clear();
+ deletedIds.clear();
try {
while (c.moveToNext()) {
String serverId = c.getString(Message.LIST_SERVER_ID_COLUMN);
// Keep going if there's no serverId
if (serverId == null) {
continue;
+ // Also check if this message is referenced elsewhere
+ } else if (messageReferenced(cr, c.getLong(Message.CONTENT_ID_COLUMN))) {
+ userLog("Postponing deletion of referenced message: ", serverId);
+ continue;
} else if (first) {
s.start(Tags.SYNC_COMMANDS);
first = false;
}
// Send the command to delete this message
s.start(Tags.SYNC_DELETE).data(Tags.SYNC_SERVER_ID, serverId).end();
- mDeletedIdList.add(c.getLong(Message.LIST_ID_COLUMN));
+ deletedIds.add(c.getLong(Message.LIST_ID_COLUMN));
}
} finally {
c.close();
}
+ return first;
+ }
+
+ @Override
+ public boolean sendLocalChanges(Serializer s) throws IOException {
+ ContentResolver cr = mContext.getContentResolver();
+
+ // Never upsync from these folders
+ if (mMailbox.mType == Mailbox.TYPE_DRAFTS || mMailbox.mType == Mailbox.TYPE_OUTBOX) {
+ return false;
+ }
+
+ // This code is split out for unit testing purposes
+ boolean firstCommand = sendDeletedItems(s, mDeletedIdList, true);
+
// Find our trash mailbox, since deletions will have been moved there...
long trashMailboxId =
Mailbox.findMailboxOfType(mContext, mMailbox.mAccountKey, Mailbox.TYPE_TRASH);
// Do the same now for updated items
- c = cr.query(Message.UPDATED_CONTENT_URI, Message.LIST_PROJECTION,
+ Cursor c = cr.query(Message.UPDATED_CONTENT_URI, Message.LIST_PROJECTION,
MessageColumns.MAILBOX_KEY + '=' + mMailbox.mId, null, null);
// We keep track of the list of updated item id's as we did above with deleted items
@@ -627,9 +670,9 @@ public class EmailSyncAdapter extends AbstractSyncAdapter {
}
// If the message is now in the trash folder, it has been deleted by the user
if (currentCursor.getLong(UPDATES_MAILBOX_KEY_COLUMN) == trashMailboxId) {
- if (first) {
+ if (firstCommand) {
s.start(Tags.SYNC_COMMANDS);
- first = false;
+ firstCommand = false;
}
// Send the command to delete this message
s.start(Tags.SYNC_DELETE).data(Tags.SYNC_SERVER_ID, serverId).end();
@@ -659,9 +702,9 @@ public class EmailSyncAdapter extends AbstractSyncAdapter {
continue;
}
- if (first) {
+ if (firstCommand) {
s.start(Tags.SYNC_COMMANDS);
- first = false;
+ firstCommand = false;
}
// Send the change to "read" and "favorite" (flagged)
s.start(Tags.SYNC_CHANGE)
@@ -708,7 +751,7 @@ public class EmailSyncAdapter extends AbstractSyncAdapter {
c.close();
}
- if (!first) {
+ if (!firstCommand) {
s.end(); // SYNC_COMMANDS
}
return false;