Reimplement reply/forward to use mTextReply/mHtmlReply

* Move creation of final reply/forward text (i.e. new text plus
  the original) to Rfc822Output, where it belongs.
* Prepares the way for use of SmartForward/SmartReply in
  Exchange and replying w/ multipart/alternative in SMTP
* Moved test from MessageCompose to new Rfc822OutputTests, and note
  that new tests should be added (this is not a regression; there
  were never enough tests here)

Change-Id: Ibefb842f47cc9223714856d99b8d4f55b55f49e3
This commit is contained in:
Marc Blank 2009-09-12 14:45:08 -07:00
parent c7897acf56
commit f2dded3a2f
4 changed files with 122 additions and 84 deletions

View File

@ -72,8 +72,6 @@ import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class MessageCompose extends Activity implements OnClickListener, OnFocusChangeListener {
@ -101,9 +99,6 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
private static final int ACTIVITY_REQUEST_PICK_ATTACHMENT = 1;
private static final Pattern PATTERN_START_OF_LINE = Pattern.compile("(?m)^");
private static final Pattern PATTERN_ENDLINE_CRLF = Pattern.compile("\r\n");
private static final String[] ATTACHMENT_META_COLUMNS = {
OpenableColumns.DISPLAY_NAME,
OpenableColumns.SIZE
@ -572,45 +567,6 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
return addresses;
}
/*
* Takes care to append source info and text in a REPLY or FORWARD situation.
*/
/* package */ String buildBodyText(Message sourceMessage) {
/*
* Build the Body that will contain the text of the message. We'll decide where to
* include it later.
*/
final String action = getIntent().getAction();
String text = mMessageContentView.getText().toString();
if (mQuotedTextBar.getVisibility() == View.VISIBLE && sourceMessage != null) {
String quotedText = sourceMessage.mText;
if (quotedText != null) {
// fix CR-LF line endings to LF-only needed by EditText.
Matcher matcher = PATTERN_ENDLINE_CRLF.matcher(quotedText);
quotedText = matcher.replaceAll("\n");
}
String fromAsString = Address.unpackToString(sourceMessage.mFrom);
if (ACTION_REPLY.equals(action) || ACTION_REPLY_ALL.equals(action)) {
text += getString(R.string.message_compose_reply_header_fmt, fromAsString);
if (quotedText != null) {
Matcher matcher = PATTERN_START_OF_LINE.matcher(quotedText);
text += matcher.replaceAll(">");
}
} else if (ACTION_FORWARD.equals(action)) {
String subject = sourceMessage.mSubject;
String to = Address.unpackToString(sourceMessage.mTo);
String cc = Address.unpackToString(sourceMessage.mCc);
text += getString(R.string.message_compose_fwd_header_fmt, subject, fromAsString,
to != null ? to : "", cc != null ? cc : "");
if (quotedText != null) {
text += quotedText;
}
}
}
return text;
}
private ContentValues getUpdateContentValues(Message message) {
ContentValues values = new ContentValues();
values.put(MessageColumns.TIMESTAMP, message.mTimeStamp);
@ -656,15 +612,14 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
* @param account the account (used to obtain From: address).
* @param bodyText the body text.
*/
private void updateMessage(Message message, Account account, String bodyText,
boolean hasAttachments) {
private void updateMessage(Message message, Account account, boolean hasAttachments) {
message.mTimeStamp = System.currentTimeMillis();
message.mFrom = new Address(account.getEmailAddress(), account.getSenderName()).pack();
message.mTo = getPackedAddresses(mToView);
message.mCc = getPackedAddresses(mCcView);
message.mBcc = getPackedAddresses(mBccView);
message.mSubject = mSubjectView.getText().toString();
message.mText = bodyText;
message.mText = mMessageContentView.getText().toString();
message.mAccountKey = account.mId;
message.mDisplayName = makeDisplayName(message.mTo, message.mCc, message.mBcc);
message.mFlagLoaded = Message.FLAG_LOADED_COMPLETE;
@ -672,13 +627,19 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
String action = getIntent().getAction();
// Use the Intent to set flags saying this message is a reply or a forward and save the
// unique id of the source message
if (mSource != null) {
if (ACTION_REPLY.equals(action) || ACTION_REPLY_ALL.equals(action)) {
message.mFlags |= Message.FLAG_TYPE_REPLY;
if (mSource != null && mQuotedTextBar.getVisibility() == View.VISIBLE) {
if (ACTION_REPLY.equals(action) || ACTION_REPLY_ALL.equals(action)
|| ACTION_FORWARD.equals(action)) {
message.mSourceKey = mSource.mId;
} else if (ACTION_FORWARD.equals(action)) {
// Get the body of the source message here
// Note that the following commented line will be useful when we use HTML in replies
//message.mHtmlReply = mSource.mHtml;
message.mTextReply = mSource.mText;
}
if (ACTION_FORWARD.equals(action)) {
message.mFlags |= Message.FLAG_TYPE_FORWARD;
message.mSourceKey = mSource.mId;
} else {
message.mFlags |= Message.FLAG_TYPE_REPLY;
}
}
}
@ -704,7 +665,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
mDraft = new Message();
}
final Attachment[] attachments = getAttachmentsFromUI();
updateMessage(mDraft, mAccount, buildBodyText(mSource), attachments.length > 0);
updateMessage(mDraft, mAccount, attachments.length > 0);
mSaveMessageTask = new AsyncTask<Void, Void, Void>() {
@Override
@ -714,6 +675,8 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus
mDraft.update(MessageCompose.this, getUpdateContentValues(mDraft));
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);
Body.updateBodyWithMessageId(MessageCompose.this, mDraft.mId, values);
} else {
// mDraft.mId is set upon return of saveToMailbox()

View File

@ -16,6 +16,7 @@
package com.android.email.mail.transport;
import com.android.email.R;
import com.android.email.codec.binary.Base64;
import com.android.email.codec.binary.Base64OutputStream;
import com.android.email.mail.Address;
@ -42,17 +43,53 @@ import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Utility class to output RFC 822 messages from provider email messages
*/
public class Rfc822Output {
private static final Pattern PATTERN_START_OF_LINE = Pattern.compile("(?m)^");
private static final Pattern PATTERN_ENDLINE_CRLF = Pattern.compile("\r\n");
// In MIME, en_US-like date format should be used. In other words "MMM" should be encoded to
// "Jan", not the other localized format like "Ene" (meaning January in locale es).
static final SimpleDateFormat mDateFormat =
new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.US);
/*package*/ static String buildBodyText(Context context, Message message) {
int flags = message.mFlags;
Body body = Body.restoreBodyWithMessageId(context, message.mId);
String text = body.mTextContent;
String quotedText = body.mTextReply;
if (quotedText != null) {
// fix CR-LF line endings to LF-only needed by EditText.
Matcher matcher = PATTERN_ENDLINE_CRLF.matcher(quotedText);
quotedText = matcher.replaceAll("\n");
}
String fromAsString = Address.unpackToString(message.mFrom);
if ((flags & Message.FLAG_TYPE_REPLY) != 0) {
text += context.getString(R.string.message_compose_reply_header_fmt, fromAsString);
if (quotedText != null) {
Matcher matcher = PATTERN_START_OF_LINE.matcher(quotedText);
text += matcher.replaceAll(">");
}
} else if ((flags & Message.FLAG_TYPE_FORWARD) != 0) {
String subject = message.mSubject;
String to = Address.unpackToString(message.mTo);
String cc = Address.unpackToString(message.mCc);
text += context.getString(R.string.message_compose_fwd_header_fmt, subject,
fromAsString, to != null ? to : "", cc != null ? cc : "");
if (quotedText != null) {
text += quotedText;
}
}
return text;
}
/**
* Write the entire message to an output stream. This method provides buffering, so it is
* not necessary to pass in a buffered output stream here.
@ -92,7 +129,7 @@ public class Rfc822Output {
writeAddressHeader(writer, "Reply-To", message.mReplyTo);
// Analyze message and determine if we have multiparts
String text = Body.restoreBodyTextWithMessageId(context, messageId);
String text = buildBodyText(context, message);
Uri uri = ContentUris.withAppendedId(Attachment.MESSAGE_ID_URI, messageId);
Cursor attachmentsCursor = context.getContentResolver().query(uri,

View File

@ -164,36 +164,7 @@ public class MessageComposeInstrumentationTests
assertEquals(0, mMessageView.length());
}
/**
* Test for buildBodyText().
* Compare with expected values.
* Also test the situation where the message has no body.
*/
public void testBuildBodyText() throws MessagingException, Throwable {
final Message message = buildTestMessage(RECIPIENT_TO, SENDER, SUBJECT, BODY);
Intent intent = new Intent(ACTION_REPLY);
final MessageCompose a = getActivity();
a.setIntent(intent);
runTestOnUiThread(new Runnable() {
public void run() {
a.processSourceMessage(message, null);
String body = a.buildBodyText(message);
assertEquals(REPLY_BODY, body);
}
});
message.mText = null;
runTestOnUiThread(new Runnable() {
public void run() {
a.processSourceMessage(message, null);
String body = a.buildBodyText(message);
assertEquals(REPLY_BODY_SHORT, body);
}
});
}
/**
/**
* Test a couple of variations of processSourceMessage() for REPLY
* To = Reply-To or From: (if REPLY)
* To = (Reply-To or From:) + To: + Cc: (if REPLY_ALL)

View File

@ -0,0 +1,67 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.email.mail.transport;
import com.android.email.provider.EmailContent.Message;
import android.test.AndroidTestCase;
/**
* Tests of the Rfc822Output (used for sending mail)
*
* You can run this entire test case with:
* runtest -c com.android.email.mail.transport.Rfc822OutputTests email
*/
public class Rfc822OutputTests extends AndroidTestCase {
private static final String SENDER = "sender@android.com";
private static final String REPLYTO = "replyto@android.com";
private static final String RECIPIENT_TO = "recipient-to@android.com";
private static final String RECIPIENT_CC = "recipient-cc@android.com";
private static final String RECIPIENT_BCC = "recipient-bcc@android.com";
private static final String SUBJECT = "This is the subject";
private static final String BODY = "This is the body. This is also the body.";
private static final String REPLY_BODY_SHORT = "\n\n" + SENDER + " wrote:\n\n";
private static final String REPLY_BODY = REPLY_BODY_SHORT + ">" + BODY;
// TODO Create more tests here. Specifically, we should test to make sure that forward works
// properly instead of just reply
/**
* Test for buildBodyText().
* Compare with expected values.
* Also test the situation where the message has no body.
*/
public void testBuildBodyText() {
// Create the least necessary; sender, flags, and the body of the reply
Message msg = new Message();
msg.mText = "";
msg.mFrom = SENDER;
msg.mFlags = Message.FLAG_TYPE_REPLY;
msg.mTextReply = BODY;
msg.save(getContext());
String body = Rfc822Output.buildBodyText(getContext(), msg);
assertEquals(REPLY_BODY, body);
// Save a different message with no reply body (so we reset the id)
msg.mId = -1;
msg.mTextReply = null;
msg.save(getContext());
body = Rfc822Output.buildBodyText(getContext(), msg);
assertEquals(REPLY_BODY_SHORT, body);
}
}