问题:进入邮箱,新建一邮件-添加附件-保存为草稿,进入草稿箱,点击此邮件-再添加一附件-保存草稿,进入草稿箱查看附件少了一个
[操作步骤]
1、ST[Email]进入邮箱,新建一邮件-添加附件-保存为草稿
2、进入草稿箱,点击此邮件-再添加一附件-保存草稿
3、进入草稿箱查看附件少了一个
解决思路:
1.通过界面找到菜单里的"保存草稿"
2.通过搜索"保存草稿"找到对应的id为save
3.通过搜索R.id.save找到其在ComposeActivity.java的位置
else if (id == R.id.save) {doSave(true);}
4.点击进入doSave(true)方法
private void doSave(boolean showToast) {
sendOrSaveWithSanityChecks(true, showToast, false, false);
}
5.点击进入sendOrSaveWithSanityChecks(true, showToast, false, false)方法
protected boolean sendOrSaveWithSanityChecks(final boolean save, final boolean showToast,
final boolean orientationChanged, final boolean autoSend) {
//判断用户以及是否自动发送
if (mAccounts == null || mAccount == null) {
Toast.makeText(this, R.string.send_failed, Toast.LENGTH_SHORT).show();
if (autoSend) {
finish();
}
return false;
}
//验证收件人,发件人,抄送人
final String[] to, cc, bcc;
if (orientationChanged) {
to = cc = bcc = new String[0];
} else {
to = getToAddresses();
cc = getCcAddresses();
bcc = getBccAddresses();
}
// Don't let the user send to nobody (but it's okay to save a message
// with no recipients)
if (!save && (to.length == 0 && cc.length == 0 && bcc.length == 0)) {
showRecipientErrorDialog(getString(R.string.recipient_needed));
return false;
}
List<String> wrongEmails = new ArrayList<String>();
if (!save) {
checkInvalidEmails(to, wrongEmails);
checkInvalidEmails(cc, wrongEmails);
checkInvalidEmails(bcc, wrongEmails);
}
// Don't let the user send an email with invalid recipients
if (wrongEmails.size() > 0) {
String errorText = String.format(getString(R.string.invalid_recipient),
wrongEmails.get(0));
showRecipientErrorDialog(errorText);
return false;
}
//一些异常情况处理
// Show a warning before sending only if there are no attachments.
if (!save) {
if (mAttachmentsView.getAttachments().isEmpty() && showEmptyTextWarnings()) {
boolean warnAboutEmptySubject = isSubjectEmpty();
boolean emptyBody = TextUtils.getTrimmedLength(mBodyView.getEditableText()) == 0;
// A warning about an empty body may not be warranted when
// forwarding mails, since a common use case is to forward
// quoted text and not append any more text.
boolean warnAboutEmptyBody = emptyBody && (!mForward || isBodyEmpty());
// When we bring up a dialog warning the user about a send,
// assume that they accept sending the message. If they do not,
// the dialog listener is required to enable sending again.
if (warnAboutEmptySubject) {
showSendConfirmDialog(R.string.confirm_send_message_with_no_subject, save,
showToast);
return true;
}
if (warnAboutEmptyBody) {
showSendConfirmDialog(R.string.confirm_send_message_with_no_body, save,
showToast);
return true;
}
}
// Ask for confirmation to send (if always required)
if (showSendConfirmation()) {
showSendConfirmDialog(R.string.confirm_send_message, save, showToast);
return true;
}
}
sendOrSave(save, showToast);
return true;
}
通过分析以上代码,可以发现sendOrSave(save, showToast)为主要代码
5.点击进入sendOrSave(save, showToast)方法
private void sendOrSave(final boolean save, final boolean showToast) {
//判断当前设备是否被Monkey乱点中
// Check if user is a monkey. Monkeys can compose and hit send
// button but are not allowed to send anything off the device.
if (ActivityManager.isUserAMonkey()) {
return;
}
final Spanned body = mBodyView.getEditableText();
SendOrSaveCallback callback = new SendOrSaveCallback() {
// FIXME: unused
private int mRestoredRequestId;
@Override
public void initializeSendOrSave(SendOrSaveTask sendOrSaveTask) {
synchronized (mActiveTasks) {
int numTasks = mActiveTasks.size();
if (numTasks == 0) {
// Start service so we won't be killed if this app is
// put in the background.
startService(new Intent(ComposeActivity.this, EmptyService.class));
}
mActiveTasks.add(sendOrSaveTask);
}
if (sTestSendOrSaveCallback != null) {
sTestSendOrSaveCallback.initializeSendOrSave(sendOrSaveTask);
}
}
@Override
public void notifyMessageIdAllocated(SendOrSaveMessage sendOrSaveMessage,
Message message) {
synchronized (mDraftLock) {
mDraftAccount = sendOrSaveMessage.mAccount;
mDraftId = message.id;
mDraft = message;
if (sRequestMessageIdMap != null) {
sRequestMessageIdMap.put(sendOrSaveMessage.requestId(), mDraftId);
}
// Cache request message map, in case the process is killed
saveRequestMap();
}
if (sTestSendOrSaveCallback != null) {
sTestSendOrSaveCallback.notifyMessageIdAllocated(sendOrSaveMessage, message);
}
}
@Override
public Message getMessage() {
synchronized (mDraftLock) {
return mDraft;
}
}
@Override
public void sendOrSaveFinished(SendOrSaveTask task, boolean success) {
// Update the last sent from account.
if (mAccount != null) {
MailAppProvider.getInstance().setLastSentFromAccount(mAccount.uri.toString());
}
if (success) {
// Successfully sent or saved so reset change markers
discardChanges();
} else {
// A failure happened with saving/sending the draft
// TODO(pwestbro): add a better string that should be used
// when failing to send or save
Toast.makeText(ComposeActivity.this, R.string.send_failed, Toast.LENGTH_SHORT)
.show();
}
int numTasks;
synchronized (mActiveTasks) {
// Remove the task from the list of active tasks
mActiveTasks.remove(task);
numTasks = mActiveTasks.size();
}
if (numTasks == 0) {
// Stop service so we can be killed.
stopService(new Intent(ComposeActivity.this, EmptyService.class));
}
if (sTestSendOrSaveCallback != null) {
sTestSendOrSaveCallback.sendOrSaveFinished(task, success);
}
}
};
setAccount(mReplyFromAccount.account);
if (mSendSaveTaskHandler == null) {
HandlerThread handlerThread = new HandlerThread("Send Message Task Thread");
handlerThread.start();
mSendSaveTaskHandler = new Handler(handlerThread.getLooper());
}
Message msg = createMessage(mReplyFromAccount, getMode());
mRequestId = sendOrSaveInternal(this, mReplyFromAccount, msg, mRefMessage, body,
mQuotedTextView.getQuotedTextIfIncluded(), callback,
mSendSaveTaskHandler, save, mComposeMode, mDraftAccount, mExtraValues);
// Don't display the toast if the user is just changing the orientation,
// but we still need to save the draft to the cursor because this is how we restore
// the attachments when the configuration change completes.
if (showToast && (getChangingConfigurations() & ActivityInfo.CONFIG_ORIENTATION) == 0) {
Toast.makeText(this, save ? R.string.message_saved : R.string.sending_message,
Toast.LENGTH_LONG).show();
}
// Need to update variables here because the send or save completes
// asynchronously even though the toast shows right away.
discardChanges();
updateSaveUi();
// If we are sending, finish the activity
if (!save) {
finish();
}
}
可以看到在该方法的内部类里有个sendOrSaveFinished方法,该方法是保存完毕的回调方法,通过搜索该方法可以发现在SendOrSaveTask类里有回调该方法
@VisibleForTesting
public static class SendOrSaveTask implements Runnable {
private final Context mContext;
@VisibleForTesting
public final SendOrSaveCallback mSendOrSaveCallback;
@VisibleForTesting
public final SendOrSaveMessage mSendOrSaveMessage;
private ReplyFromAccount mExistingDraftAccount;
public SendOrSaveTask(Context context, SendOrSaveMessage message,
SendOrSaveCallback callback, ReplyFromAccount draftAccount) {
mContext = context;
mSendOrSaveCallback = callback;
mSendOrSaveMessage = message;
mExistingDraftAccount = draftAccount;
}
@Override
public void run() {
final SendOrSaveMessage sendOrSaveMessage = mSendOrSaveMessage;
final ReplyFromAccount selectedAccount = sendOrSaveMessage.mAccount;
Message message = mSendOrSaveCallback.getMessage();
long messageId = message != null ? message.id : UIProvider.INVALID_MESSAGE_ID;
// If a previous draft has been saved, in an account that is different
// than what the user wants to send from, remove the old draft, and treat this
// as a new message
if (mExistingDraftAccount != null
&& !selectedAccount.account.uri.equals(mExistingDraftAccount.account.uri)) {
if (messageId != UIProvider.INVALID_MESSAGE_ID) {
ContentResolver resolver = mContext.getContentResolver();
ContentValues values = new ContentValues();
values.put(BaseColumns._ID, messageId);
if (mExistingDraftAccount.account.expungeMessageUri != null) {
new ContentProviderTask.UpdateTask()
.run(resolver, mExistingDraftAccount.account.expungeMessageUri,
values, null, null);
} else {
// TODO(mindyp) delete the conversation.
}
// reset messageId to 0, so a new message will be created
messageId = UIProvider.INVALID_MESSAGE_ID;
}
}
final long messageIdToSave = messageId;
sendOrSaveMessage(messageIdToSave, sendOrSaveMessage, selectedAccount);
if (!sendOrSaveMessage.mSave) {
incrementRecipientsTimesContacted(mContext,
(String) sendOrSaveMessage.mValues.get(UIProvider.MessageColumns.TO));
incrementRecipientsTimesContacted(mContext,
(String) sendOrSaveMessage.mValues.get(UIProvider.MessageColumns.CC));
incrementRecipientsTimesContacted(mContext,
(String) sendOrSaveMessage.mValues.get(UIProvider.MessageColumns.BCC));
}
mSendOrSaveCallback.sendOrSaveFinished(SendOrSaveTask.this, true);
}
在该方法之前调用sendOrSaveMessage(messageIdToSave, sendOrSaveMessage, selectedAccount)方法,点击进入该方法
/**
* Send or Save a message.
*/
private void sendOrSaveMessage(final long messageIdToSave,
final SendOrSaveMessage sendOrSaveMessage, final ReplyFromAccount selectedAccount) {
final ContentResolver resolver = mContext.getContentResolver();
final boolean updateExistingMessage = messageIdToSave != UIProvider.INVALID_MESSAGE_ID;
final String accountMethod = sendOrSaveMessage.mSave ?
UIProvider.AccountCallMethods.SAVE_MESSAGE :
UIProvider.AccountCallMethods.SEND_MESSAGE;
try {
if (updateExistingMessage) {
sendOrSaveMessage.mValues.put(BaseColumns._ID, messageIdToSave);
callAccountSendSaveMethod(resolver,
selectedAccount.account, accountMethod, sendOrSaveMessage);
} else {
Uri messageUri = null;
final Bundle result = callAccountSendSaveMethod(resolver,
selectedAccount.account, accountMethod, sendOrSaveMessage);
if (result != null) {
// If a non-null value was returned, then the provider handled the call
// method
messageUri = result.getParcelable(UIProvider.MessageColumns.URI);
}
if (sendOrSaveMessage.mSave && messageUri != null) {
final Cursor messageCursor = resolver.query(messageUri,
UIProvider.MESSAGE_PROJECTION, null, null, null);
if (messageCursor != null) {
try {
if (messageCursor.moveToFirst()) {
// Broadcast notification that a new message has
// been allocated
mSendOrSaveCallback.notifyMessageIdAllocated(sendOrSaveMessage,
new Message(messageCursor));
}
} finally {
messageCursor.close();
}
}
}
}
} finally {
// Close any opened file descriptors
closeOpenedAttachmentFds(sendOrSaveMessage);
}
}
发现该方法里有调用方法callAccountSendSaveMethod(resolver,selectedAccount.account, accountMethod, sendOrSaveMessage),点击进入该方法
/**
* Use the {@link ContentResolver#call} method to send or save the message.
*
* If this was successful, this method will return an non-null Bundle instance
*/
private static Bundle callAccountSendSaveMethod(final ContentResolver resolver,
final Account account, final String method,
final SendOrSaveMessage sendOrSaveMessage) {
// Copy all of the values from the content values to the bundle
final Bundle methodExtras = new Bundle(sendOrSaveMessage.mValues.size());
final Set<Entry<String, Object>> valueSet = sendOrSaveMessage.mValues.valueSet();
for (Entry<String, Object> entry : valueSet) {
final Object entryValue = entry.getValue();
final String key = entry.getKey();
if (entryValue instanceof String) {
methodExtras.putString(key, (String)entryValue);
} else if (entryValue instanceof Boolean) {
methodExtras.putBoolean(key, (Boolean)entryValue);
} else if (entryValue instanceof Integer) {
methodExtras.putInt(key, (Integer)entryValue);
} else if (entryValue instanceof Long) {
methodExtras.putLong(key, (Long)entryValue);
} else {
LogUtils.wtf(LOG_TAG, "Unexpected object type: %s",
entryValue.getClass().getName());
}
}
// If the SendOrSaveMessage has some opened fds, add them to the bundle
final Bundle fdMap = sendOrSaveMessage.attachmentFds();
if (fdMap != null) {
methodExtras.putParcelable(
UIProvider.SendOrSaveMethodParamKeys.OPENED_FD_MAP, fdMap);
}
return resolver.call(account.uri, method, account.uri.toString(), methodExtras);
}
}
该方法是用来把数据保存到bundle,最后调用了方法resolver.call(account.uri, method, account.uri.toString(), methodExtras),点击进入该方法
/**
* Call a provider-defined method. This can be used to implement
* read or write interfaces which are cheaper than using a Cursor and/or
* do not fit into the traditional table model.
*
* @param method provider-defined method name to call. Opaque to
* framework, but must be non-null.
* @param arg provider-defined String argument. May be null.
* @param extras provider-defined Bundle argument. May be null.
* @return a result Bundle, possibly null. Will be null if the ContentProvider
* does not implement call.
* @throws NullPointerException if uri or method is null
* @throws IllegalArgumentException if uri is not known
*/
public final Bundle call(Uri uri, String method, String arg, Bundle extras) {
if (uri == null) {
throw new NullPointerException("uri == null");
}
if (method == null) {
throw new NullPointerException("method == null");
}
IContentProvider provider = acquireProvider(uri);
if (provider == null) {
throw new IllegalArgumentException("Unknown URI " + uri);
}
try {
return provider.call(mPackageName, method, arg, extras);
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
return null;
} finally {
releaseProvider(provider);
}
}
发现在该方法内回调了provider.call方法,点击进入该方法(ContentProvider.java)
@Override
public Bundle call(String callingPkg, String method, String arg, Bundle extras) {
final String original = setCallingPackage(callingPkg);
try {
return ContentProvider.this.call(method, arg, extras);
} finally {
setCallingPackage(original);
}
}
发现在该方法又调用了回调方法ContentProvider.this.call,点击进入该回调方法(EmailProvider.java)
@Override
public Bundle call(String method, String arg, Bundle extras) {
LogUtils.d(TAG, "EmailProvider#call(%s, %s)", method, arg);
// Handle queries for the device friendly name.
// TODO: This should eventually be a device property, not defined by the app.
if (TextUtils.equals(method, EmailContent.DEVICE_FRIENDLY_NAME)) {
final Bundle bundle = new Bundle(1);
// TODO: For now, just use the model name since we don't yet have a user-supplied name.
bundle.putString(EmailContent.DEVICE_FRIENDLY_NAME, Build.MODEL);
return bundle;
}
// Handle sync status callbacks.
if (TextUtils.equals(method, SYNC_STATUS_CALLBACK_METHOD)) {
updateSyncStatus(extras);
return null;
}
if (TextUtils.equals(method, MailboxUtilities.FIX_PARENT_KEYS_METHOD)) {
fixParentKeys(getDatabase(getContext()));
return null;
}
// Handle send & save.
final Uri accountUri = Uri.parse(arg);
final long accountId = Long.parseLong(accountUri.getPathSegments().get(1));
Uri messageUri = null;
if (TextUtils.equals(method, UIProvider.AccountCallMethods.SEND_MESSAGE)) {
messageUri = uiSendDraftMessage(accountId, extras);
Preferences.getPreferences(getContext()).setLastUsedAccountId(accountId);
} else if (TextUtils.equals(method, UIProvider.AccountCallMethods.SAVE_MESSAGE)) {
messageUri = uiSaveDraftMessage(accountId, extras);
} else if (TextUtils.equals(method, UIProvider.AccountCallMethods.SET_CURRENT_ACCOUNT)) {
LogUtils.d(TAG, "Unhandled (but expected) Content provider method: %s", method);
} else {
LogUtils.wtf(TAG, "Unexpected Content provider method: %s", method);
}
final Bundle result;
if (messageUri != null) {
result = new Bundle(1);
result.putParcelable(UIProvider.MessageColumns.URI, messageUri);
} else {
result = null;
}
return result;
}
发现该方法判断语句里有调用uiSaveDraftMessage方法
else if (TextUtils.equals(method, UIProvider.AccountCallMethods.SAVE_MESSAGE)) {
messageUri = uiSaveDraftMessage(accountId, extras);
}
点击进入uiSaveDraftMessage方法
private Uri uiSaveDraftMessage(final long accountId, final Bundle extras) {
final Mailbox mailbox =
getMailboxByAccountIdAndType(accountId, Mailbox.TYPE_DRAFTS);
if (mailbox == null) return null;
final Message msg;
if (extras.containsKey(BaseColumns._ID)) {
final long messageId = extras.getLong(BaseColumns._ID);
msg = Message.restoreMessageWithId(getContext(), messageId);
} else {
msg = new Message();
}
return uiSaveMessage(msg, mailbox, extras);
}
发现该方法在返回的时候调用了uiSaveMessage(msg, mailbox, extras)方法,点击进入该方法
/**
* Given a mailbox and the content values for a message, create/save the message in the mailbox
* @param mailbox the mailbox to use
* @param extras the bundle containing the message fields
* @return the uri of the newly created message
* TODO(yph): The following fields are available in extras but unused, verify whether they
* should be respected:
* - UIProvider.MessageColumns.SNIPPET
* - UIProvider.MessageColumns.REPLY_TO
* - UIProvider.MessageColumns.FROM
*/
private Uri uiSaveMessage(Message msg, Mailbox mailbox, Bundle extras) {
final Context context = getContext();
// Fill in the message
final Account account = Account.restoreAccountWithId(context, mailbox.mAccountKey);
if (account == null) return null;
final String customFromAddress =
extras.getString(UIProvider.MessageColumns.CUSTOM_FROM_ADDRESS);
if (!TextUtils.isEmpty(customFromAddress)) {
msg.mFrom = customFromAddress;
} else {
msg.mFrom = account.getEmailAddress();
}
msg.mTimeStamp = System.currentTimeMillis();
msg.mTo = extras.getString(UIProvider.MessageColumns.TO);
msg.mCc = extras.getString(UIProvider.MessageColumns.CC);
msg.mBcc = extras.getString(UIProvider.MessageColumns.BCC);
msg.mSubject = extras.getString(UIProvider.MessageColumns.SUBJECT);
msg.mText = extras.getString(UIProvider.MessageColumns.BODY_TEXT);
msg.mHtml = extras.getString(UIProvider.MessageColumns.BODY_HTML);
msg.mMailboxKey = mailbox.mId;
msg.mAccountKey = mailbox.mAccountKey;
msg.mDisplayName = msg.mTo;
msg.mFlagLoaded = Message.FLAG_LOADED_COMPLETE;
msg.mFlagRead = true;
msg.mFlagSeen = true;
final Integer quoteStartPos = extras.getInt(UIProvider.MessageColumns.QUOTE_START_POS);
msg.mQuotedTextStartPos = quoteStartPos == null ? 0 : quoteStartPos;
int flags = 0;
final int draftType = extras.getInt(UIProvider.MessageColumns.DRAFT_TYPE);
switch(draftType) {
case DraftType.FORWARD:
flags |= Message.FLAG_TYPE_FORWARD;
break;
case DraftType.REPLY_ALL:
flags |= Message.FLAG_TYPE_REPLY_ALL;
//$FALL-THROUGH$
case DraftType.REPLY:
flags |= Message.FLAG_TYPE_REPLY;
break;
case DraftType.COMPOSE:
flags |= Message.FLAG_TYPE_ORIGINAL;
break;
}
int draftInfo = 0;
if (extras.containsKey(UIProvider.MessageColumns.QUOTE_START_POS)) {
draftInfo = extras.getInt(UIProvider.MessageColumns.QUOTE_START_POS);
if (extras.getInt(UIProvider.MessageColumns.APPEND_REF_MESSAGE_CONTENT) != 0) {
draftInfo |= Message.DRAFT_INFO_APPEND_REF_MESSAGE;
}
}
if (!extras.containsKey(UIProvider.MessageColumns.APPEND_REF_MESSAGE_CONTENT)) {
flags |= Message.FLAG_NOT_INCLUDE_QUOTED_TEXT;
}
msg.mDraftInfo = draftInfo;
msg.mFlags = flags;
final String ref = extras.getString(UIProvider.MessageColumns.REF_MESSAGE_ID);
if (ref != null && msg.mQuotedTextStartPos >= 0) {
String refId = Uri.parse(ref).getLastPathSegment();
try {
msg.mSourceKey = Long.parseLong(refId);
} catch (NumberFormatException e) {
// This will be zero; the default
}
}
// Get attachments from the ContentValues
final List<com.android.mail.providers.Attachment> uiAtts =
com.android.mail.providers.Attachment.fromJSONArray(
extras.getString(UIProvider.MessageColumns.ATTACHMENTS));
final ArrayList<Attachment> atts = new ArrayList<Attachment>();
boolean hasUnloadedAttachments = false;
Bundle attachmentFds =
extras.getParcelable(UIProvider.SendOrSaveMethodParamKeys.OPENED_FD_MAP);
for (com.android.mail.providers.Attachment uiAtt: uiAtts) {
final Uri attUri = uiAtt.uri;
if (attUri != null && attUri.getAuthority().equals(EmailContent.AUTHORITY)) {
// If it's one of ours, retrieve the attachment and add it to the list
final long attId = Long.parseLong(attUri.getLastPathSegment());
final Attachment att = Attachment.restoreAttachmentWithId(context, attId);
if (att != null) {
// We must clone the attachment into a new one for this message; easiest to
// use a parcel here
final Parcel p = Parcel.obtain();
att.writeToParcel(p, 0);
p.setDataPosition(0);
final Attachment attClone = new Attachment(p);
p.recycle();
// Clear the messageKey (this is going to be a new attachment)
attClone.mMessageKey = 0;
// If we're sending this, it's not loaded, and we're not smart forwarding
// add the download flag, so that ADS will start up
if (mailbox.mType == Mailbox.TYPE_OUTBOX && att.getContentUri() == null &&
((account.mFlags & Account.FLAGS_SUPPORTS_SMART_FORWARD) == 0)) {
attClone.mFlags |= Attachment.FLAG_DOWNLOAD_FORWARD;
hasUnloadedAttachments = true;
}
atts.add(attClone);
}
} else {
// Cache the attachment. This will allow us to send it, if the permissions are
// revoked.
final String cachedFileUri =
AttachmentUtils.cacheAttachmentUri(context, uiAtt, attachmentFds);
// Convert external attachment to one of ours and add to the list
atts.add(convertUiAttachmentToAttachment(uiAtt, cachedFileUri, msg.mAccountKey));
}
}
if (!atts.isEmpty()) {
msg.mAttachments = atts;
msg.mFlagAttachment = true;
if (hasUnloadedAttachments) {
Utility.showToast(context, R.string.message_view_attachment_background_load);
}
}
// Save it or update it...
if (!msg.isSaved()) {
msg.save(context);
} else {
// This is tricky due to how messages/attachments are saved; rather than putz with
// what's changed, we'll delete/re-add them
final ArrayList<ContentProviderOperation> ops =
new ArrayList<ContentProviderOperation>();
// Delete all existing attachments
EmailContent.Attachment[] attachments = EmailContent.Attachment
.restoreAttachmentsWithMessageId(context, msg.mId);
ops.add(ContentProviderOperation.newDelete(
ContentUris.withAppendedId(Attachment.MESSAGE_ID_URI, msg.mId))
.build());
// Delete the body
ops.add(ContentProviderOperation.newDelete(Body.CONTENT_URI)
.withSelection(Body.MESSAGE_KEY + "=?", new String[] {Long.toString(msg.mId)})
.build());
if (attachments != null) {
for (int i = 0; i < attachments.length; i++) {
boolean hasExist = false;
if (attachments.length > 1) {
for (int j = 0; j < i; j++) {
if (attachments[j].mFileName
.equals(attachments[i].mFileName)) {
hasExist = true;
break;
}
}
}
if (!hasExist && mailbox.mType == Mailbox.TYPE_DRAFTS) {
ops.add(ContentProviderOperation
.newInsert(Attachment.CONTENT_URI)
.withValues(attachments[i].toContentValues())
.build());
}
}
}
// Add the ops for the message, atts, and body
msg.addSaveOps(ops);
// Do it!
try {
applyBatch(ops);
} catch (OperationApplicationException e) {
LogUtils.d(TAG, "applyBatch exception");
}
}
notifyUIMessage(msg.mId);
if (mailbox.mType == Mailbox.TYPE_OUTBOX) {
startSync(mailbox, 0);
final long originalMsgId = msg.mSourceKey;
if (originalMsgId != 0) {
final Message originalMsg = Message.restoreMessageWithId(context, originalMsgId);
// If the original message exists, set its forwarded/replied to flags
if (originalMsg != null) {
final ContentValues cv = new ContentValues();
flags = originalMsg.mFlags;
switch(draftType) {
case DraftType.FORWARD:
flags |= Message.FLAG_FORWARDED;
break;
case DraftType.REPLY_ALL:
case DraftType.REPLY:
flags |= Message.FLAG_REPLIED_TO;
break;
}
cv.put(Message.FLAGS, flags);
context.getContentResolver().update(ContentUris.withAppendedId(
Message.CONTENT_URI, originalMsgId), cv, null, null);
}
}
}
return uiUri("uimessage", msg.mId);
}
在该方法里面我们发现为了防止附件重复每次进入都会先清除一遍,然后再添加,所以应该在清除之前保存一下,清除之后再插入进去。
所以在上面的代码处加入了如下代码
EmailContent.Attachment[] attachments = EmailContent.Attachment
.restoreAttachmentsWithMessageId(context, msg.mId);
if (attachments != null) {
for (int i = 0; i < attachments.length; i++) {
boolean hasExist = false;
if (attachments.length > 1) {
for (int j = 0; j < i; j++) {
if (attachments[j].mFileName
.equals(attachments[i].mFileName)) {
hasExist = true;
break;
}
}
}
if (!hasExist && mailbox.mType == Mailbox.TYPE_DRAFTS) {
ops.add(ContentProviderOperation
.newInsert(Attachment.CONTENT_URI)
.withValues(attachments[i].toContentValues())
.build());
}
}
}