diff options
| author | yingying <yingying@codeaurora.org> | 2014-04-02 19:33:28 +0800 |
|---|---|---|
| committer | LorDClockaN <davor@losinj.com> | 2014-08-27 22:26:17 +0200 |
| commit | 505a0cfc994e642287494da30f0d124209e6f154 (patch) | |
| tree | 66b62a04227ec7dae6ad19d18dcc588296fd27b2 | |
| parent | b428212be452f58c1dba9556805c0b1dbbd241cf (diff) | |
Exchange: Add the dummy attachment as load more place holder.
- Add the place holder attachment for the uncomplete mail.
- Update the body's html content for viewable attachments.
CRs-fixed: 642921
Change-Id: Iba64b5ac7cfd4f02d948bc3cb4eb400d568a4165
| -rw-r--r-- | src/com/android/exchange/adapter/EmailSyncAdapter.java | 99 | ||||
| -rw-r--r-- | src/com/android/exchange/adapter/EmailSyncParser.java | 95 | ||||
| -rw-r--r-- | src/com/android/exchange/adapter/LoadMore.java | 10 |
3 files changed, 182 insertions, 22 deletions
diff --git a/src/com/android/exchange/adapter/EmailSyncAdapter.java b/src/com/android/exchange/adapter/EmailSyncAdapter.java index 0527c3d9..db66edfa 100644 --- a/src/com/android/exchange/adapter/EmailSyncAdapter.java +++ b/src/com/android/exchange/adapter/EmailSyncAdapter.java @@ -20,6 +20,7 @@ package com.android.exchange.adapter; import android.content.ContentProviderOperation; +import android.content.ContentProviderResult; import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; @@ -48,6 +49,7 @@ import com.android.emailcommon.provider.EmailContent; import com.android.emailcommon.provider.EmailContent.AccountColumns; import com.android.emailcommon.provider.EmailContent.Attachment; import com.android.emailcommon.provider.EmailContent.Body; +import com.android.emailcommon.provider.EmailContent.BodyColumns; import com.android.emailcommon.provider.EmailContent.MailboxColumns; import com.android.emailcommon.provider.EmailContent.Message; import com.android.emailcommon.provider.EmailContent.MessageColumns; @@ -558,7 +560,7 @@ public class EmailSyncAdapter extends AbstractSyncAdapter { msg.mFlagRead = getValueInt() == 1; break; case Tags.BASE_BODY: - bodyParser(msg); + bodyParser(atts, msg); break; case Tags.EMAIL_FLAG: msg.mFlagFavorite = flagParser(); @@ -780,9 +782,13 @@ public class EmailSyncAdapter extends AbstractSyncAdapter { return state; } - private void bodyParser(Message msg) throws IOException { + private void bodyParser(ArrayList<EmailContent.Attachment> atts, + Message msg) throws IOException { String bodyType = Eas.BODY_PREFERENCE_TEXT; String body = ""; + String length = null; + boolean truncated = false; + while (nextTag(Tags.EMAIL_BODY) != END) { switch (tag) { case Tags.BASE_TYPE: @@ -792,10 +798,10 @@ public class EmailSyncAdapter extends AbstractSyncAdapter { body = getValue(); break; case Tags.BASE_TRUNCATED: - boolean truncated = getValue().equals("1") ? true : false; - if (truncated) { - msg.mFlagLoaded = Message.FLAG_LOADED_PARTIAL_COMPLETE; - } + truncated = getValueInt() == 1; + break; + case Tags.BASE_ESTIMATED_DATA_SIZE: + length = getValue(); break; default: skipTag(); @@ -807,6 +813,20 @@ public class EmailSyncAdapter extends AbstractSyncAdapter { } else { msg.mText = body; } + + // If the content is truncated, we will insert one dummy attachment + // as the placeholder which is same as POP3 and IMAP. More details + // please refer to the Utilities in the Email. + if (truncated) { + msg.mFlagLoaded = Message.FLAG_LOADED_PARTIAL_COMPLETE; + EmailContent.Attachment att = new EmailContent.Attachment(); + att.mFileName = ""; + att.mSize = Long.parseLong(length); + att.mMimeType = "text/plain"; + att.mAccountKey = mAccount.mId; + att.mFlags = Attachment.FLAG_DUMMY_ATTACHMENT; + atts.add(att); + } } /** @@ -898,6 +918,9 @@ public class EmailSyncAdapter extends AbstractSyncAdapter { // contentId rather than contentLocation, when sent from Ex03, Ex07, and Ex10 if (isInline && !TextUtils.isEmpty(contentId)) { att.mContentId = contentId; + } else { + // This isn't the viewable part, set the local message has attachment. + msg.mFlagAttachment = true; } // Check if this attachment can't be downloaded due to an account policy if (mPolicy != null) { @@ -908,7 +931,6 @@ public class EmailSyncAdapter extends AbstractSyncAdapter { } } atts.add(att); - msg.mFlagAttachment = true; } } @@ -1187,11 +1209,18 @@ public class EmailSyncAdapter extends AbstractSyncAdapter { .withSelection(EmailContent.RECORD_ID + "=?", bindArgument) .withValue(Message.FLAG_LOADED, Message.FLAG_LOADED_COMPLETE) .build()); + // apply these ops first. + applyBatch(ops, tryCount); } } for (Message msg: newEmails) { msg.addSaveOps(ops); + // apply these ops and get the result. + ContentProviderResult[] res = applyBatch(ops, tryCount); + // After the message and attachments saved, we need update the message body's + // HTML content for the viewable parts. + updateBodyForInlineAttachment(ops, res, msg, tryCount); } for (Long id : deletedEmails) { @@ -1240,6 +1269,62 @@ public class EmailSyncAdapter extends AbstractSyncAdapter { } } + private ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> ops, + int tryCount) { + ContentProviderResult[] res = null; + try { + res = mContentResolver.applyBatch(EmailContent.AUTHORITY, ops); + ops.clear(); + } catch (TransactionTooLargeException e) { + LogUtils.w(TAG, "Transaction failed on fetched message; retrying..."); + commitImpl(++tryCount); + } catch (RemoteException e) { + // There is nothing to be done here; fail by returning null + } catch (OperationApplicationException e) { + // There is nothing to be done here; fail by returning null + } + + return res; + } + + private void updateBodyForInlineAttachment(ArrayList<ContentProviderOperation> ops, + ContentProviderResult[] res, EmailContent.Message msg, int tryCount) { + if (msg == null || msg.mHtml == null || res == null) { + // There isn't HTML content, do nothing. + return; + } + + // Find the message's id. + long msgId = -1; + try { + for (ContentProviderResult result : res) { + if (result.uri.getPath().startsWith( + EmailContent.Message.CONTENT_URI.getPath())) { + msgId = Long.parseLong(result.uri.getLastPathSegment()); + break; + } + } + } catch (NumberFormatException e) { + // Meet the format exception. + return; + } finally { + if (msgId < 0) return; + } + + // Get the attachments to update the HTML content. + String newContent = Message.updateHTMLContentForInlineAtts(mContext, msg.mHtml, msgId); + if (newContent != null) { + long bodyId = Body.lookupBodyIdWithMessageId(mContext, msgId); + ContentValues cv = new ContentValues(); + cv.put(BodyColumns.HTML_CONTENT, newContent); + ops.add(ContentProviderOperation.newUpdate( + ContentUris.withAppendedId(EmailContent.Body.CONTENT_URI, bodyId)) + .withValues(cv) + .build()); + applyBatch(ops, tryCount); + } + } + @Override protected void wipe() { // This file is deprecated, no need to implement this. diff --git a/src/com/android/exchange/adapter/EmailSyncParser.java b/src/com/android/exchange/adapter/EmailSyncParser.java index b3f9ac1f..c2febfd3 100644 --- a/src/com/android/exchange/adapter/EmailSyncParser.java +++ b/src/com/android/exchange/adapter/EmailSyncParser.java @@ -1,6 +1,7 @@ package com.android.exchange.adapter; import android.content.ContentProviderOperation; +import android.content.ContentProviderResult; import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; @@ -30,6 +31,9 @@ import com.android.emailcommon.provider.EmailContent; import com.android.emailcommon.provider.Mailbox; import com.android.emailcommon.provider.Policy; import com.android.emailcommon.provider.ProviderUnavailableException; +import com.android.emailcommon.provider.EmailContent.Attachment; +import com.android.emailcommon.provider.EmailContent.Body; +import com.android.emailcommon.provider.EmailContent.BodyColumns; import com.android.emailcommon.provider.EmailContent.Message; import com.android.emailcommon.utility.AttachmentUtilities; import com.android.emailcommon.utility.ConversionUtilities; @@ -164,7 +168,7 @@ public class EmailSyncParser extends AbstractSyncParser { msg.mFlagRead = getValueInt() == 1; break; case Tags.BASE_BODY: - bodyParser(msg); + bodyParser(atts, msg); break; case Tags.EMAIL_FLAG: msg.mFlagFavorite = flagParser(); @@ -386,9 +390,13 @@ public class EmailSyncParser extends AbstractSyncParser { return state; } - private void bodyParser(EmailContent.Message msg) throws IOException { + private void bodyParser(ArrayList<EmailContent.Attachment> atts, + EmailContent.Message msg) throws IOException { String bodyType = Eas.BODY_PREFERENCE_TEXT; String body = ""; + String length = null; + boolean truncated = false; + while (nextTag(Tags.EMAIL_BODY) != END) { switch (tag) { case Tags.BASE_TYPE: @@ -398,10 +406,10 @@ public class EmailSyncParser extends AbstractSyncParser { body = getValue(); break; case Tags.BASE_TRUNCATED: - boolean truncated = getValue().equals("1") ? true : false; - if (truncated) { - msg.mFlagLoaded = Message.FLAG_LOADED_PARTIAL_COMPLETE; - } + truncated = getValueInt() == 1; + break; + case Tags.BASE_ESTIMATED_DATA_SIZE: + length = getValue(); break; default: skipTag(); @@ -413,6 +421,20 @@ public class EmailSyncParser extends AbstractSyncParser { } else { msg.mText = body; } + + // If the content is truncated, we will insert one dummy attachment + // as the placeholder which is same as POP3 and IMAP. More details + // please refer to the Utilities in the Email. + if (truncated) { + msg.mFlagLoaded = Message.FLAG_LOADED_PARTIAL_COMPLETE; + EmailContent.Attachment att = new EmailContent.Attachment(); + att.mFileName = ""; + att.mSize = Long.parseLong(length); + att.mMimeType = "text/plain"; + att.mAccountKey = mAccount.mId; + att.mFlags = Attachment.FLAG_DUMMY_ATTACHMENT; + atts.add(att); + } } /** @@ -507,6 +529,9 @@ public class EmailSyncParser extends AbstractSyncParser { // contentId rather than contentLocation, when sent from Ex03, Ex07, and Ex10 if (isInline && !TextUtils.isEmpty(contentId)) { att.mContentId = contentId; + } else { + // This isn't the viewable part, set the local message has attachment. + msg.mFlagAttachment = true; } // Check if this attachment can't be downloaded due to an account policy if (mPolicy != null) { @@ -517,7 +542,6 @@ public class EmailSyncParser extends AbstractSyncParser { } } atts.add(att); - msg.mFlagAttachment = true; } } @@ -839,12 +863,17 @@ public class EmailSyncParser extends AbstractSyncParser { EmailContent.Message.FLAG_LOADED_COMPLETE) .build()); } - applyBatchIfNeeded(ops, maxOpsPerBatch, false); + // As we need get the new msg's id, so we will force to apply batch for these ops. + applyBatchIfNeeded(ops, maxOpsPerBatch, true); } for (EmailContent.Message msg: newEmails) { msg.addSaveOps(ops); - applyBatchIfNeeded(ops, maxOpsPerBatch, false); + // As we need get the new msg's id, so we will force to apply batch for these ops. + ContentProviderResult[] res = applyBatchIfNeeded(ops, maxOpsPerBatch, true); + // After the message and attachments saved, we need update the message body's + // HTML content for the viewable parts. + updateBodyForInlineAttachment(ops, res, msg); } for (Long id : deletedEmails) { @@ -888,10 +917,11 @@ public class EmailSyncParser extends AbstractSyncParser { // Check if there at least MAX_OPS_PER_BATCH ops in queue and flush if there are. // If force is true, flush regardless of size. - private void applyBatchIfNeeded(ArrayList<ContentProviderOperation> ops, int maxOpsPerBatch, - boolean force) + private ContentProviderResult[] applyBatchIfNeeded(ArrayList<ContentProviderOperation> ops, + int maxOpsPerBatch, boolean force) throws RemoteException, OperationApplicationException { - if (force || ops.size() >= maxOpsPerBatch) { + ContentProviderResult[] res = null; + if (force || ops.size() >= maxOpsPerBatch) { // STOPSHIP Remove calculating size of data before ship if (LogUtils.isLoggable(TAG, Log.DEBUG)) { final Parcel parcel = Parcel.obtain(); @@ -902,8 +932,47 @@ public class EmailSyncParser extends AbstractSyncParser { ops.size(), parcel.dataSize())); parcel.recycle(); } - mContentResolver.applyBatch(EmailContent.AUTHORITY, ops); + res = mContentResolver.applyBatch(EmailContent.AUTHORITY, ops); ops.clear(); } + return res; + } + + private void updateBodyForInlineAttachment(ArrayList<ContentProviderOperation> ops, + ContentProviderResult[] res, EmailContent.Message msg) + throws RemoteException, OperationApplicationException { + if (msg == null || msg.mHtml == null || res == null) { + // There isn't HTML content, do nothing. + return; + } + + // Find the message's id. + long msgId = -1; + try { + for (ContentProviderResult result : res) { + if (result.uri.getPath().startsWith(EmailContent.Message.CONTENT_URI.getPath())) { + msgId = Long.parseLong(result.uri.getLastPathSegment()); + break; + } + } + } catch (NumberFormatException e) { + // Meet the format exception. + return; + } finally { + if (msgId < 0) return; + } + + // Get the attachments to update the HTML content. + String newContent = Message.updateHTMLContentForInlineAtts(mContext, msg.mHtml, msgId); + if (newContent != null) { + long bodyId = Body.lookupBodyIdWithMessageId(mContext, msgId); + ContentValues cv = new ContentValues(); + cv.put(BodyColumns.HTML_CONTENT, newContent); + ops.add(ContentProviderOperation.newUpdate( + ContentUris.withAppendedId(EmailContent.Body.CONTENT_URI, bodyId)) + .withValues(cv) + .build()); + applyBatchIfNeeded(ops, 0, true /* force */); + } } }
\ No newline at end of file diff --git a/src/com/android/exchange/adapter/LoadMore.java b/src/com/android/exchange/adapter/LoadMore.java index f697ee0b..1c508566 100644 --- a/src/com/android/exchange/adapter/LoadMore.java +++ b/src/com/android/exchange/adapter/LoadMore.java @@ -142,7 +142,7 @@ public class LoadMore { LoadMoreParser parser = new LoadMoreParser(is, msg); parser.parse(); if (parser.getStatusCode() == LoadMoreParser.STATUS_CODE_SUCCESS) { - parser.commit(cr); + parser.commit(context); } } } else { @@ -196,16 +196,22 @@ public class LoadMore { } // commit the body data to database. - public void commit(ContentResolver contentResolver) { + public void commit(Context context) { LogUtils.d(Logging.LOG_TAG, "Fetched message body successfully for " + mMessage.mId); // update the body data + String newContent = Message.updateHTMLContentForInlineAtts(context, + mMessage.mHtml, mMessage.mId); + if (newContent != null) { + mMessage.mHtml = newContent; + } ContentValues cv = new ContentValues(); if (mBodyType.equals(Eas.BODY_PREFERENCE_HTML)) { cv.put(BodyColumns.HTML_CONTENT, mMessage.mHtml); } else { cv.put(BodyColumns.TEXT_CONTENT, mMessage.mText); } + ContentResolver contentResolver = context.getContentResolver(); int res = contentResolver.update(Body.CONTENT_URI, cv, BodyColumns.MESSAGE_KEY + "=" + mMessage.mId, null); LogUtils.d(Logging.LOG_TAG, "update the body content, success number : " + res); |
