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:
parent
6a7d10e1c3
commit
afb34d19ee
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user