Rework reply/forward to use unified-style data

* Deprecate fields no longer used in Body table

Bug: 6393061

Change-Id: I48f41063667f6edef43756b16acdfd1e65ef554c
This commit is contained in:
Marc Blank 2012-04-27 16:05:24 -07:00
parent 6a7d10e1c3
commit afb34d19ee
4 changed files with 35 additions and 124 deletions

View File

@ -20,8 +20,6 @@ import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.text.Html;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Base64OutputStream;
@ -52,9 +50,6 @@ import java.util.regex.Pattern;
*/
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).
private static final SimpleDateFormat DATE_FORMAT =
@ -69,11 +64,6 @@ public class Rfc822Output {
Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
/** Match group in {@code BODDY_PATTERN} for the body HTML */
private static final int BODY_PATTERN_GROUP = 1;
/** Pattern to find both dos and unix newlines */
private static final Pattern NEWLINE_PATTERN =
Pattern.compile("\\r?\\n");
/** HTML string to use when replacing text newlines */
private static final String NEWLINE_HTML = "<br>";
/** Index of the plain text version of the message body */
private final static int INDEX_BODY_TEXT = 0;
/** Index of the HTML version of the message body */
@ -95,78 +85,20 @@ public class Rfc822Output {
}
}
/**
* Returns an HTML encoded message alternate
*/
/*package*/ static String getHtmlAlternate(Body body, boolean useSmartReply) {
if (body.mHtmlReply == null) {
return null;
}
StringBuffer altMessage = new StringBuffer();
String htmlContent = TextUtils.htmlEncode(body.mTextContent); // Escape HTML reserved chars
htmlContent = NEWLINE_PATTERN.matcher(htmlContent).replaceAll(NEWLINE_HTML);
altMessage.append(htmlContent);
if (body.mIntroText != null) {
String htmlIntro = TextUtils.htmlEncode(body.mIntroText);
htmlIntro = NEWLINE_PATTERN.matcher(htmlIntro).replaceAll(NEWLINE_HTML);
altMessage.append(htmlIntro);
}
if (!useSmartReply) {
String htmlBody = getHtmlBody(body.mHtmlReply);
altMessage.append(htmlBody);
}
return altMessage.toString();
}
/**
* Gets both the plain text and HTML versions of the message body.
*/
/*package*/ static String[] buildBodyText(Body body, int flags, boolean useSmartReply) {
String[] messageBody = new String[] { null, null };
if (body == null) {
return messageBody;
return new String[2];
}
String text = body.mTextContent;
boolean isReply = (flags & Message.FLAG_TYPE_REPLY) != 0;
boolean isForward = (flags & Message.FLAG_TYPE_FORWARD) != 0;
// For all forwards/replies, we add the intro text
if (isReply || isForward) {
String intro = body.mIntroText == null ? "" : body.mIntroText;
text += intro;
}
if (useSmartReply) {
// useSmartReply is set to true for use by SmartReply/SmartForward in EAS.
// SmartForward doesn't put a break between the original and new text, so we add an LF
if (isForward) {
text += "\n";
String[] messageBody = new String[] { body.mTextContent, body.mHtmlContent };
if (useSmartReply && body.mQuotedTextStartPos > 0) {
if (messageBody[0] != null) {
messageBody[0] = messageBody[0].substring(0, body.mQuotedTextStartPos);
} else if (messageBody[1] != null) {
messageBody[1] = messageBody[1].substring(0, body.mQuotedTextStartPos);
}
} else {
String quotedText = body.mTextReply;
// If there is no plain-text body, use de-tagified HTML as the text body
if (quotedText == null && body.mHtmlReply != null) {
quotedText = Html.fromHtml(body.mHtmlReply).toString();
}
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");
}
if (isReply) {
if (quotedText != null) {
Matcher matcher = PATTERN_START_OF_LINE.matcher(quotedText);
text += matcher.replaceAll(">");
}
} else if (isForward) {
if (quotedText != null) {
text += quotedText;
}
}
}
messageBody[INDEX_BODY_TEXT] = text;
// Exchange 2003 doesn't seem to support multipart w/SmartReply and SmartForward, so
// we'll skip this. Really, it would only matter if we could compose HTML replies
if (!useSmartReply) {
messageBody[INDEX_BODY_HTML] = getHtmlAlternate(body, useSmartReply);
}
return messageBody;
}
@ -250,7 +182,7 @@ public class Rfc822Output {
writer.write("\r\n");
// first multipart element is the body
if (bodyText[INDEX_BODY_TEXT] != null) {
if (bodyText[INDEX_BODY_TEXT] != null || bodyText[INDEX_BODY_HTML] != null) {
writeBoundary(writer, multipartBoundary, false);
writeTextWithHeaders(writer, stream, bodyText);
}
@ -398,9 +330,7 @@ public class Rfc822Output {
}
/**
* Write the body text. If only one version of the body is specified (either plain text
* or HTML), the text is written directly. Otherwise, the plain text and HTML bodies
* are both written with the appropriate headers.
* Write the body text.
*
* Note this always uses base64, even when not required. Slightly less efficient for
* US-ASCII text, but handles all formats even when non-ascii chars are involved. A small
@ -412,49 +342,23 @@ public class Rfc822Output {
*/
private static void writeTextWithHeaders(Writer writer, OutputStream out, String[] bodyText)
throws IOException {
boolean html = false;
String text = bodyText[INDEX_BODY_TEXT];
String html = bodyText[INDEX_BODY_HTML];
if (text == null) {
text = bodyText[INDEX_BODY_HTML];
html = true;
}
if (text == null) {
writer.write("\r\n"); // a truly empty message
} else {
String multipartBoundary = null;
boolean multipart = html != null;
// Simplified case for no multipart - just emit text and be done.
if (multipart) {
// continue with multipart headers, then into multipart body
multipartBoundary = getNextBoundary();
writeHeader(writer, "Content-Type",
"multipart/alternative; boundary=\"" + multipartBoundary + "\"");
// Finish headers and prepare for body section(s)
writer.write("\r\n");
writeBoundary(writer, multipartBoundary, false);
}
// first multipart element is the body
writeHeader(writer, "Content-Type", "text/plain; charset=utf-8");
String mimeType = "text/" + (html ? "html" : "plain");
writeHeader(writer, "Content-Type", mimeType + "; charset=utf-8");
writeHeader(writer, "Content-Transfer-Encoding", "base64");
writer.write("\r\n");
byte[] textBytes = text.getBytes("UTF-8");
writer.flush();
out.write(Base64.encode(textBytes, Base64.CRLF));
if (multipart) {
// next multipart section
writeBoundary(writer, multipartBoundary, false);
writeHeader(writer, "Content-Type", "text/html; charset=utf-8");
writeHeader(writer, "Content-Transfer-Encoding", "base64");
writer.write("\r\n");
byte[] htmlBytes = html.getBytes("UTF-8");
writer.flush();
out.write(Base64.encode(htmlBytes, Base64.CRLF));
// end of multipart section
writeBoundary(writer, multipartBoundary, true);
}
}
}

View File

@ -260,14 +260,17 @@ public abstract class EmailContent {
// The plain text content itself
public static final String TEXT_CONTENT = "textContent";
// Replied-to or forwarded body (in html form)
@Deprecated
public static final String HTML_REPLY = "htmlReply";
// Replied-to or forwarded body (in text form)
@Deprecated
public static final String TEXT_REPLY = "textReply";
// 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
@Deprecated
public static final String INTRO_TEXT = "introText";
// The start of quoted text within our text content
public static final String QUOTED_TEXT_START_POS = "quotedTextStartPos";
@ -283,9 +286,12 @@ public abstract class EmailContent {
public static final int CONTENT_MESSAGE_KEY_COLUMN = 1;
public static final int CONTENT_HTML_CONTENT_COLUMN = 2;
public static final int CONTENT_TEXT_CONTENT_COLUMN = 3;
@Deprecated
public static final int CONTENT_HTML_REPLY_COLUMN = 4;
@Deprecated
public static final int CONTENT_TEXT_REPLY_COLUMN = 5;
public static final int CONTENT_SOURCE_KEY_COLUMN = 6;
@Deprecated
public static final int CONTENT_INTRO_TEXT_COLUMN = 7;
public static final int CONTENT_QUOTED_TEXT_START_POS_COLUMN = 8;
@ -301,12 +307,15 @@ public abstract class EmailContent {
public static final String[] COMMON_PROJECTION_HTML = new String[] {
RECORD_ID, BodyColumns.HTML_CONTENT
};
@Deprecated
public static final String[] COMMON_PROJECTION_REPLY_TEXT = new String[] {
RECORD_ID, BodyColumns.TEXT_REPLY
};
@Deprecated
public static final String[] COMMON_PROJECTION_REPLY_HTML = new String[] {
RECORD_ID, BodyColumns.HTML_REPLY
};
@Deprecated
public static final String[] COMMON_PROJECTION_INTRO = new String[] {
RECORD_ID, BodyColumns.INTRO_TEXT
};
@ -321,7 +330,9 @@ public abstract class EmailContent {
public long mMessageKey;
public String mHtmlContent;
public String mTextContent;
@Deprecated
public String mHtmlReply;
@Deprecated
public String mTextReply;
public int mQuotedTextStartPos;
@ -331,6 +342,7 @@ public abstract class EmailContent {
* want to include quoted text.
*/
public long mSourceKey;
@Deprecated
public String mIntroText;
public Body() {
@ -445,14 +457,17 @@ public abstract class EmailContent {
return restoreTextWithMessageId(context, messageId, Body.COMMON_PROJECTION_HTML);
}
@Deprecated
public static String restoreReplyTextWithMessageId(Context context, long messageId) {
return restoreTextWithMessageId(context, messageId, Body.COMMON_PROJECTION_REPLY_TEXT);
}
@Deprecated
public static String restoreReplyHtmlWithMessageId(Context context, long messageId) {
return restoreTextWithMessageId(context, messageId, Body.COMMON_PROJECTION_REPLY_HTML);
}
@Deprecated
public static String restoreIntroTextWithMessageId(Context context, long messageId) {
return restoreTextWithMessageId(context, messageId, Body.COMMON_PROJECTION_INTRO);
}
@ -963,18 +978,9 @@ public abstract class EmailContent {
if (mHtml != null) {
cv.put(Body.HTML_CONTENT, mHtml);
}
if (mTextReply != null) {
cv.put(Body.TEXT_REPLY, mTextReply);
}
if (mHtmlReply != null) {
cv.put(Body.HTML_REPLY, mHtmlReply);
}
if (mSourceKey != 0) {
cv.put(Body.SOURCE_MESSAGE_KEY, mSourceKey);
}
if (mIntroText != null) {
cv.put(Body.INTRO_TEXT, mIntroText);
}
if (mQuotedTextStartPos != 0) {
cv.put(Body.QUOTED_TEXT_START_POS, mQuotedTextStartPos);
}

View File

@ -35,7 +35,7 @@ import java.util.List;
/**
* Encapsulates commonly used attachment information related to suitability for viewing and saving,
* based on the attachment's filename and mime type.
* based on the attachment's filename and mimetype.
*/
public class AttachmentInfo {
// Projection which can be used with the constructor taking a Cursor argument

View File

@ -3043,7 +3043,8 @@ outer:
msg.mDisplayName = msg.mTo;
msg.mFlagLoaded = Message.FLAG_LOADED_COMPLETE;
msg.mFlagRead = true;
msg.mQuotedTextStartPos = values.getAsInteger(UIProvider.MessageColumns.QUOTE_START_POS);
Integer quoteStartPos = values.getAsInteger(UIProvider.MessageColumns.QUOTE_START_POS);
msg.mQuotedTextStartPos = quoteStartPos == null ? 0 : quoteStartPos;
int flags = 0;
int draftType = values.getAsInteger(UIProvider.MessageColumns.DRAFT_TYPE);
switch(draftType) {
@ -3062,7 +3063,7 @@ outer:
}
msg.mFlags = flags;
String ref = values.getAsString(UIProvider.MessageColumns.REF_MESSAGE_ID);
if (ref != null) {
if (ref != null && msg.mQuotedTextStartPos > 0) {
String refId = Uri.parse(ref).getLastPathSegment();
try {
long sourceKey = Long.parseLong(refId);