編寫好郵件點擊發送,代碼執行MessageCompose.java 中的(郵件的編寫,及添加附件都在這個類中處理)java
private void sendOrSaveMessage(boolean send) { if (!mMessageLoaded) { Log.w(Logging.LOG_TAG, "Attempted to save draft message prior to the state being fully loaded"); return; } synchronized (sActiveSaveTasks) { mLastSaveTaskId = sNextSaveTaskId++; SendOrSaveMessageTask task = new SendOrSaveMessageTask(mLastSaveTaskId, send); // Ensure the tasks are executed serially so that rapid scheduling doesn't result // in inconsistent data.// task.executeSerial(); } }
private class SendOrSaveMessageTask extends EmailAsyncTask<Void, Void, Long> { private final boolean mSend; private final long mTaskId; /** A context that will survive even past activity destruction. */ private final Context mContext; public SendOrSaveMessageTask(long taskId, boolean send) { super(null /* DO NOT cancel in onDestroy */); if (send && ActivityManager.isUserAMonkey()) { Log.d(Logging.LOG_TAG, "Inhibiting send while monkey is in charge."); send = false; } mTaskId = taskId; mSend = send; mContext = getApplicationContext(); sActiveSaveTasks.put(mTaskId, this); } @Override protected Long doInBackground(Void... params) { synchronized (mDraft) { updateMessage(mDraft, mAccount, mAttachments.size() > 0, mSend); ContentResolver resolver = getContentResolver(); if (mDraft.isSaved()) { // Update the message Uri draftUri = ContentUris.withAppendedId(Message.SYNCED_CONTENT_URI, mDraft.mId); resolver.update(draftUri, getUpdateContentValues(mDraft), null, null); // Update the body ContentValues values = new ContentValues(); values.put(BodyColumns.TEXT_CONTENT, mDraft.mText); values.put(BodyColumns.TEXT_REPLY, mDraft.mTextReply); values.put(BodyColumns.HTML_REPLY, mDraft.mHtmlReply); values.put(BodyColumns.INTRO_TEXT, mDraft.mIntroText); values.put(BodyColumns.SOURCE_MESSAGE_KEY, mDraft.mSourceKey); Body.updateBodyWithMessageId(MessageCompose.this, mDraft.mId, values); } else { // mDraft.mId is set upon return of saveToMailbox() mController.saveToMailbox(mDraft, Mailbox.TYPE_DRAFTS); } // For any unloaded attachment, set the flag saying we need it loaded boolean hasUnloadedAttachments = false; //mAttachments 處理附件部分 -----開始 for (Attachment attachment : mAttachments) { if (attachment.mContentUri == null && ((attachment.mFlags & Attachment.FLAG_SMART_FORWARD) == 0)) { attachment.mFlags |= Attachment.FLAG_DOWNLOAD_FORWARD; hasUnloadedAttachments = true; if (Email.DEBUG) { Log.d(Logging.LOG_TAG, "Requesting download of attachment #" + attachment.mId); } } // Make sure the UI version of the attachment has the now-correct id; we will // use the id again when coming back from picking new attachments if (!attachment.isSaved()) { // this attachment is new so save it to DB. attachment.mMessageKey = mDraft.mId; attachment.save(MessageCompose.this); } else if (attachment.mMessageKey != mDraft.mId) { // We clone the attachment and save it again; otherwise, it will // continue to point to the source message. From this point forward, // the attachments will be independent of the original message in the // database; however, we still need the message on the server in order // to retrieve unloaded attachments attachment.mMessageKey = mDraft.mId; //attachment.toContentValues()是最重要的,由於在toContentValues()這裏 是將全部關於附件的信息都put 到了 //ContentValues 中,而後執行 insert後,發送的郵件附件信息就存儲在了數據表中,也就是在toContentValues中的時候 //中文名就會出現解碼 亂碼問題,具體看下面貼出的代碼部分。 ContentValues cv = attachment.toContentValues(); cv.put(Attachment.FLAGS, attachment.mFlags); cv.put(Attachment.MESSAGE_KEY, mDraft.mId); getContentResolver().insert(Attachment.CONTENT_URI, cv); } } //處理附件部分 -----結束 if (mSend) { // Let the user know if message sending might be delayed by background // downlading of unloaded attachments if (hasUnloadedAttachments) { Utility.showToast(MessageCompose.this, R.string.message_view_attachment_background_load); } mController.sendMessage(mDraft); ArrayList<CharSequence> addressTexts = new ArrayList<CharSequence>(); addressTexts.add(mToView.getText()); addressTexts.add(mCcView.getText()); addressTexts.add(mBccView.getText()); DataUsageStatUpdater updater = new DataUsageStatUpdater(mContext); updater.updateWithRfc822Address(addressTexts); } return mDraft.mId; } } private boolean shouldShowSaveToast() { // Don't show the toast when rotating, or when opening an Activity on top of this one. return !isChangingConfigurations() && !mPickingAttachment; } @Override protected void onSuccess(Long draftId) { // Note that send or save tasks are always completed, even if the activity // finishes earlier. sActiveSaveTasks.remove(mTaskId); // Don't display the toast if the user is just changing the orientation if (!mSend && shouldShowSaveToast()) { Toast.makeText(mContext, R.string.message_saved_toast, Toast.LENGTH_LONG).show(); } } }
就上面附件註釋部分說的,附件信息解析並添加到數據表中,主要是在 com.android.emailcommon.provider中的EmailContent中,裏面有對個Message 數據解析處理模塊。下面看對附件部分的解析。android
@Override public ContentValues toContentValues() { Log.i("EmailContent", "toContentValues ----> mFileName: "+mFileName+" mContentUri: "+mContentUri+"===mEncoding==="+mEncoding); //這裏就是對 附件 mFileName 爲中文時編碼處理,不然就會亂碼,至於用mEncoding是否爲空來判斷是否 //對附件名作編碼處理,是由於在解決這個問題反覆測試發現,若是PC端發郵件,android 應用端接受郵件,mEncoding會 //有編碼值,若是這裏作了加密及強制轉換編碼,那麼客戶端接受到的附件,由於作了下面處理就也會出現亂碼,因此咱們是 //不處理的。可是客戶端發出去郵件時 mEncoding始終會是NULL,最終添加到表中時,Exchange 服務類型的編碼會是 //base64,至於在什麼地方給處理的沒研究太深,POP,IMAP 的 mEncoding倒是NULL (我也不知到在哪處理的,沒時間研究啊) //若是你們發現不有特殊狀況的話,歡迎糾正。。。謝謝啦 if(TextUtils.isEmpty(mEncoding)){ mFileName = MimeUtility.foldAndEncode2(mFileName,"Content-Disposition".length() + 2); } Log.i("EmailContent", "toContentValues --1-->"+ mFileName); ContentValues values = new ContentValues(); values.put(AttachmentColumns.FILENAME, mFileName); values.put(AttachmentColumns.MIME_TYPE, mMimeType); values.put(AttachmentColumns.SIZE, mSize); values.put(AttachmentColumns.CONTENT_ID, mContentId); values.put(AttachmentColumns.CONTENT_URI, mContentUri); values.put(AttachmentColumns.MESSAGE_KEY, mMessageKey); values.put(AttachmentColumns.LOCATION, mLocation); values.put(AttachmentColumns.ENCODING, mEncoding); values.put(AttachmentColumns.CONTENT, mContent); values.put(AttachmentColumns.FLAGS, mFlags); values.put(AttachmentColumns.CONTENT_BYTES, mContentBytes); values.put(AttachmentColumns.ACCOUNT_KEY, mAccountKey); values.put(AttachmentColumns.UI_STATE, mUiState); values.put(AttachmentColumns.UI_DESTINATION, mUiDestination); values.put(AttachmentColumns.UI_DOWNLOADED_SIZE, mUiDownloadedSize); return values; }
對於上面在往表裏put 數據的時候對附件名稱作了處理那麼。在顯示名稱時固然也要解密api
以下:ide
@Override public void restore(Cursor cursor) { mBaseUri = CONTENT_URI; mId = cursor.getLong(CONTENT_ID_COLUMN); mEncoding = cursor.getString(CONTENT_ENCODING_COLUMN); mFileName= cursor.getString(CONTENT_FILENAME_COLUMN); Log.i("EmailContent", "restore-->mFileName: "+ mFileName+" mEncoding: "+mEncoding); if(TextUtils.isEmpty(mEncoding)){ mFileName = MimeUtility.unfoldAndDecode(mFileName); } Log.i("EmailContent", "restore-->mFileName: "+ mFileName); mMimeType = cursor.getString(CONTENT_MIME_TYPE_COLUMN); mSize = cursor.getLong(CONTENT_SIZE_COLUMN); mContentId = cursor.getString(CONTENT_CONTENT_ID_COLUMN); mContentUri = cursor.getString(CONTENT_CONTENT_URI_COLUMN); mMessageKey = cursor.getLong(CONTENT_MESSAGE_ID_COLUMN); mLocation = cursor.getString(CONTENT_LOCATION_COLUMN); mContent = cursor.getString(CONTENT_CONTENT_COLUMN); mFlags = cursor.getInt(CONTENT_FLAGS_COLUMN); mContentBytes = cursor.getBlob(CONTENT_CONTENT_BYTES_COLUMN); mAccountKey = cursor.getLong(CONTENT_ACCOUNT_KEY_COLUMN); mUiState = cursor.getInt(CONTENT_UI_STATE_COLUMN); mUiDestination = cursor.getInt(CONTENT_UI_DESTINATION_COLUMN); mUiDownloadedSize = cursor.getInt(CONTENT_UI_DOWNLOADED_SIZE_COLUMN); }
以上是針對Exchange 服務 中文亂碼問題解決,由於 原生代碼中,使用Exchange 登陸郵箱,發送郵件,與使用POP,IMAP不是走同一個流程,具體看Controller.java中oop
public void sendPendingMessages(long accountId) { // 1. make sure we even have an outbox, exit early if not final long outboxId = Mailbox.findMailboxOfType(mProviderContext, accountId, Mailbox.TYPE_OUTBOX); if (outboxId == Mailbox.NO_MAILBOX) { return; } //若是使用的是Exchange 帳戶service便會獲得值,這裏是根據accountId判斷是哪一個服務,不然service即是NULL // 2. dispatch as necessary IEmailService service = getServiceForAccount(accountId); if (service != null) { // Service implementation try { service.startSync(outboxId, false); } catch (RemoteException e) { // TODO Change exception handling to be consistent with however this method // is implemented for other protocols Log.d("updateMailbox", "RemoteException" + e); } } else { // MessagingController implementation sendPendingMessagesSmtp(accountId); } }
再看sendPendingMessagesSmtp() 方法中執行了
測試
mLegacyController.sendPendingMessages(account, sentboxId, mLegacyListener);
而後ui
public void sendPendingMessagesSynchronous(final Account account, long sentFolderId) { TrafficStats.setThreadStatsTag(TrafficFlags.getSmtpFlags(mContext, account)); NotificationController nc = NotificationController.getInstance(mContext); // 1. Loop through all messages in the account's outbox long outboxId = Mailbox.findMailboxOfType(mContext, account.mId, Mailbox.TYPE_OUTBOX); if (outboxId == Mailbox.NO_MAILBOX) { return; } ContentResolver resolver = mContext.getContentResolver(); Cursor c = resolver.query(EmailContent.Message.CONTENT_URI, EmailContent.Message.ID_COLUMN_PROJECTION, EmailContent.Message.MAILBOX_KEY + "=?", new String[] { Long.toString(outboxId) }, null); try { // 2. exit early if (c.getCount() <= 0) { return; } // 3. do one-time setup of the Sender & other stuff mListeners.sendPendingMessagesStarted(account.mId, -1); //注意,下面這一行即是處理POP,IMAP 服務的Sender 類,該 sender是父類 POP,IMAP最終走的是 //com.android.email.mail.transport包下的SmtpSender類,也就是下面執行的 sender.sendMessage(messageId) //最終是執行的SmtpSender中的sendMessage方法。 Sender sender = Sender.getInstance(mContext, account); Store remoteStore = Store.getInstance(account, mContext); boolean requireMoveMessageToSentFolder = remoteStore.requireCopyMessageToSentFolder(); ContentValues moveToSentValues = null; if (requireMoveMessageToSentFolder) { moveToSentValues = new ContentValues(); moveToSentValues.put(MessageColumns.MAILBOX_KEY, sentFolderId); } boolean faild =false; // 4. loop through the available messages and send them while (c.moveToNext()) { long messageId = -1; try { messageId = c.getLong(0); mListeners.sendPendingMessagesStarted(account.mId, messageId); // Don't send messages with unloaded attachments if (Utility.hasUnloadedAttachments(mContext, messageId)) { if (Email.DEBUG) { Log.d(Logging.LOG_TAG, "Can't send #" + messageId + "; unloaded attachments"); } continue; } sender.sendMessage(messageId); } catch (MessagingException me) { // report error for this message, but keep trying others if (me instanceof AuthenticationFailedException) { nc.showLoginFailedNotification(account.mId); } faild = true; handler.sendEmptyMessage(1); mListeners.sendPendingMessagesFailed(account.mId, messageId, me); continue; } // 5. move to sent, or delete Uri syncedUri = ContentUris.withAppendedId(EmailContent.Message.SYNCED_CONTENT_URI, messageId); if (requireMoveMessageToSentFolder) { // If this is a forwarded message and it has attachments, delete them, as they // duplicate information found elsewhere (on the server). This saves storage. EmailContent.Message msg = EmailContent.Message.restoreMessageWithId(mContext, messageId); if (msg != null && ((msg.mFlags & EmailContent.Message.FLAG_TYPE_FORWARD) != 0)) { AttachmentUtilities.deleteAllAttachmentFiles(mContext, account.mId, messageId); } resolver.update(syncedUri, moveToSentValues, null, null); } else { AttachmentUtilities.deleteAllAttachmentFiles(mContext, account.mId, messageId); Uri uri = ContentUris.withAppendedId(EmailContent.Message.CONTENT_URI, messageId); resolver.delete(uri, null, null); resolver.delete(syncedUri, null, null); } } // 6. report completion/success if(!faild){ handler.sendEmptyMessage(0); } mListeners.sendPendingMessagesCompleted(account.mId); nc.cancelLoginFailedNotification(account.mId); } catch (MessagingException me) { if (me instanceof AuthenticationFailedException) { nc.showLoginFailedNotification(account.mId); } handler.sendEmptyMessage(1); mListeners.sendPendingMessagesFailed(account.mId, -1, me); } finally { c.close(); } }
在 SmtpSender 的sendMessage 中有一個
this
Rfc822Output.writeTo(mContext, messageId, new EOLConvertingOutputStream(mTransport.getOutputStream()), false /* do not use smart reply */, false /* do not send BCC */);
在這裏就是對POP,IMAP 的郵件信息處理,至於這裏怎麼解決郵件發送 中文名附件出現亂碼,就參考下面的網址吧,編碼
偷懶不想寫了 ^_^,加密