Merge branch 'text-quote-to-html-div'

* text-quote-to-html-div:
  Use property to determine if we should write test debug information or not.
  Remove top margin on converted quote text, consistent with html quoted text.
  Colorize quoted text that has been converted to html. Like Thunderbird, but optimized for mobile (no right margin quote bar, thinner left margin quote bar).
  Add 1ex of margin at the bottom of converted blockquotes.
  Convert quote characters in plain text messages to blockquotes in the html version.  "Be like Thunderbird."
This commit is contained in:
Andrew Chen 2012-04-24 10:24:14 -07:00
commit 8577d1558e
2 changed files with 177 additions and 4 deletions

View File

@ -190,6 +190,12 @@ public class HtmlConverter {
return buff.toString();
}
private static final String HTML_BLOCKQUOTE_COLOR_TOKEN = "$$COLOR$$";
private static final String HTML_BLOCKQUOTE_START = "<blockquote class=\"gmail_quote\" " +
"style=\"margin: 0pt 0pt 1ex 0.8ex; border-left: 1px solid $$COLOR$$; padding-left: 1ex;\">";
private static final String HTML_BLOCKQUOTE_END = "</blockquote>";
private static final String HTML_NEWLINE = "<br />";
/**
* Convert a text string into an HTML document.
*
@ -217,14 +223,19 @@ public class HtmlConverter {
}
StringReader reader = new StringReader(text);
StringBuilder buff = new StringBuilder(text.length() + TEXT_TO_HTML_EXTRA_BUFFER_LENGTH);
int c;
boolean isStartOfLine = false; // Are we currently at the start of a line?
int quoteDepth = 0; // Number of DIVs deep we are.
int quotesThisLine = 0; // How deep we should be quoting for this line.
try {
int c;
while ((c = reader.read()) != -1) {
switch (c) {
case '\n':
// pine treats <br> as two newlines, but <br/> as one newline. Use <br/> so our messages aren't
// doublespaced.
buff.append("<br />");
buff.append(HTML_NEWLINE);
isStartOfLine = true;
quotesThisLine = 0;
break;
case '&':
buff.append("&amp;");
@ -233,11 +244,39 @@ public class HtmlConverter {
buff.append("&lt;");
break;
case '>':
buff.append("&gt;");
if(isStartOfLine) {
quotesThisLine++;
} else {
buff.append("&gt;");
}
break;
case '\r':
break;
case ' ':
if(isStartOfLine) {
// If we're still in the start of the line and we have spaces, don't output them, since they
// may be collapsed by our div-converting magic.
break;
}
default:
if(isStartOfLine) {
// Not a quote character and not a space. Content is starting now.
isStartOfLine = false;
if(K9.DEBUG) {
Log.d(K9.LOG_TAG, "currentQuoteDepth: " + quoteDepth + " quotesThisLine: " + quotesThisLine);
}
// Add/remove blockquotes by comparing this line's quotes to the previous line's quotes.
if(quotesThisLine > quoteDepth) {
for(int i = quoteDepth; i < quotesThisLine; i++) {
buff.append(HTML_BLOCKQUOTE_START.replace(HTML_BLOCKQUOTE_COLOR_TOKEN, getQuoteColor(i + 1)));
}
} else if(quotesThisLine < quoteDepth) {
for(int i = quoteDepth; i > quotesThisLine; i--) {
buff.append(HTML_BLOCKQUOTE_END);
}
}
quoteDepth = quotesThisLine;
}
buff.append((char)c);
}//switch
}
@ -245,8 +284,21 @@ public class HtmlConverter {
//Should never happen
Log.e(K9.LOG_TAG, "Could not read string to convert text to HTML:", e);
}
// Close off any quotes we may have opened.
if (quoteDepth > 0) {
for (int i = quoteDepth; i > 0; i--) {
buff.append(HTML_BLOCKQUOTE_END);
}
}
text = buff.toString();
// Make newlines at the end of blockquotes nicer by putting newlines beyond the first one outside of the
// blockquote.
text = text.replaceAll(
"\\Q" + HTML_NEWLINE + "\\E((\\Q" + HTML_NEWLINE + "\\E)+?)\\Q" + HTML_BLOCKQUOTE_END + "\\E",
HTML_BLOCKQUOTE_END + "$1"
);
// Replace lines of -,= or _ with horizontal rules
text = text.replaceAll("\\s*([-=_]{30,}+)\\s*", "<hr />");
@ -275,6 +327,35 @@ public class HtmlConverter {
return text;
}
protected static final String QUOTE_COLOR_DEFAULT = "#ccc";
protected static final String QUOTE_COLOR_LEVEL_1 = "#729fcf";
protected static final String QUOTE_COLOR_LEVEL_2 = "#ad7fa8";
protected static final String QUOTE_COLOR_LEVEL_3 = "#8ae234";
protected static final String QUOTE_COLOR_LEVEL_4 = "#fcaf3e";
protected static final String QUOTE_COLOR_LEVEL_5 = "#e9b96e";
/**
* Return an HTML hex color string for a given quote level.
* @param level Quote level
* @return Hex color string with prepended #.
*/
protected static String getQuoteColor(final int level) {
switch(level) {
case 1:
return QUOTE_COLOR_LEVEL_1;
case 2:
return QUOTE_COLOR_LEVEL_2;
case 3:
return QUOTE_COLOR_LEVEL_3;
case 4:
return QUOTE_COLOR_LEVEL_4;
case 5:
return QUOTE_COLOR_LEVEL_5;
default:
return QUOTE_COLOR_DEFAULT;
}
}
/**
* Searches for link-like text in a string and turn it into a link. Append the result to
* <tt>outputBuffer</tt>. <tt>text</tt> is not modified.
@ -1168,7 +1249,7 @@ public class HtmlConverter {
final String font = K9.messageViewFixedWidthFont()
? "monospace"
: "sans-serif";
return "<pre style=\"white-space: pre-wrap; word-wrap:break-word; font-family: " + font + "\">";
return "<pre style=\"white-space: pre-wrap; word-wrap:break-word; font-family: " + font + "; margin-top: 0px\">";
}
private static String htmlifyMessageFooter() {

View File

@ -0,0 +1,92 @@
package com.fsck.k9.helper;
import junit.framework.Assert;
import org.junit.Test;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import static junit.framework.Assert.*;
public class HtmlConverterTest {
// Useful if you want to write stuff to a file for debugging in a browser.
private static final boolean WRITE_TO_FILE = Boolean.parseBoolean(System.getProperty("k9.htmlConverterTest.writeToFile", "false"));
private static final String OUTPUT_FILE = "C:/temp/parse.html";
@Test
public void testTextQuoteToHtmlBlockquote() {
String message = "Panama!\n" +
"\n" +
"Bob Barker <bob@aol.com> wrote:\n" +
"> a canal\n" +
">\n" +
"> Dorothy Jo Gideon <dorothy@aol.com> espoused:\n" +
"> >A man, a plan...\n" +
"> Too easy!\n" +
"\n" +
"Nice job :)\n" +
">> Guess!";
String result = HtmlConverter.textToHtml(message, false);
writeToFile(result);
assertEquals("<pre style=\"white-space: pre-wrap; word-wrap:break-word; font-family: sans-serif; margin-top: 0px\">Panama!<br /><br />Bob Barker &lt;bob@aol.com&gt; wrote:<br /><blockquote class=\"gmail_quote\" style=\"margin: 0pt 0pt 1ex 0.8ex; border-left: 1px solid #729fcf; padding-left: 1ex;\">a canal<br /><br />Dorothy Jo Gideon &lt;dorothy@aol.com&gt; espoused:<br /><blockquote class=\"gmail_quote\" style=\"margin: 0pt 0pt 1ex 0.8ex; border-left: 1px solid #ad7fa8; padding-left: 1ex;\">A man, a plan...<br /></blockquote>Too easy!</blockquote><br />Nice job :)<br /><blockquote class=\"gmail_quote\" style=\"margin: 0pt 0pt 1ex 0.8ex; border-left: 1px solid #729fcf; padding-left: 1ex;\"><blockquote class=\"gmail_quote\" style=\"margin: 0pt 0pt 1ex 0.8ex; border-left: 1px solid #ad7fa8; padding-left: 1ex;\">Guess!</blockquote></blockquote></pre>", result);
}
@Test
public void testTextQuoteToHtmlBlockquoteIndented() {
String message = "*facepalm*\n" +
"\n" +
"Bob Barker <bob@aol.com> wrote:\n" +
"> A wise man once said...\n" +
">\n" +
"> LOL F1RST!!!!!\n" +
">\n" +
"> :)";
String result = HtmlConverter.textToHtml(message, false);
writeToFile(result);
assertEquals("<pre style=\"white-space: pre-wrap; word-wrap:break-word; font-family: sans-serif; margin-top: 0px\">*facepalm*<br /><br />Bob Barker &lt;bob@aol.com&gt; wrote:<br /><blockquote class=\"gmail_quote\" style=\"margin: 0pt 0pt 1ex 0.8ex; border-left: 1px solid #729fcf; padding-left: 1ex;\">A wise man once said...<br /><br />LOL F1RST!!!!!<br /><br />:)</blockquote></pre>", result);
}
@Test
public void testQuoteDepthColor() {
assertEquals(HtmlConverter.getQuoteColor(1), HtmlConverter.QUOTE_COLOR_LEVEL_1);
assertEquals(HtmlConverter.getQuoteColor(2), HtmlConverter.QUOTE_COLOR_LEVEL_2);
assertEquals(HtmlConverter.getQuoteColor(3), HtmlConverter.QUOTE_COLOR_LEVEL_3);
assertEquals(HtmlConverter.getQuoteColor(4), HtmlConverter.QUOTE_COLOR_LEVEL_4);
assertEquals(HtmlConverter.getQuoteColor(5), HtmlConverter.QUOTE_COLOR_LEVEL_5);
assertEquals(HtmlConverter.getQuoteColor(-1), HtmlConverter.QUOTE_COLOR_DEFAULT);
assertEquals(HtmlConverter.getQuoteColor(0), HtmlConverter.QUOTE_COLOR_DEFAULT);
assertEquals(HtmlConverter.getQuoteColor(6), HtmlConverter.QUOTE_COLOR_DEFAULT);
String message = "zero\n" +
"> one\n" +
">> two\n" +
">>> three\n" +
">>>> four\n" +
">>>>> five\n" +
">>>>>> six";
String result = HtmlConverter.textToHtml(message, false);
writeToFile(result);
assertEquals("<pre style=\"white-space: pre-wrap; word-wrap:break-word; font-family: sans-serif; margin-top: 0px\">zero<br /><blockquote class=\"gmail_quote\" style=\"margin: 0pt 0pt 1ex 0.8ex; border-left: 1px solid #729fcf; padding-left: 1ex;\">one<br /><blockquote class=\"gmail_quote\" style=\"margin: 0pt 0pt 1ex 0.8ex; border-left: 1px solid #ad7fa8; padding-left: 1ex;\">two<br /><blockquote class=\"gmail_quote\" style=\"margin: 0pt 0pt 1ex 0.8ex; border-left: 1px solid #8ae234; padding-left: 1ex;\">three<br /><blockquote class=\"gmail_quote\" style=\"margin: 0pt 0pt 1ex 0.8ex; border-left: 1px solid #fcaf3e; padding-left: 1ex;\">four<br /><blockquote class=\"gmail_quote\" style=\"margin: 0pt 0pt 1ex 0.8ex; border-left: 1px solid #e9b96e; padding-left: 1ex;\">five<br /><blockquote class=\"gmail_quote\" style=\"margin: 0pt 0pt 1ex 0.8ex; border-left: 1px solid #ccc; padding-left: 1ex;\">six</blockquote></blockquote></blockquote></blockquote></blockquote></blockquote></pre>", result);
}
private void writeToFile(final String content) {
if(!WRITE_TO_FILE) {
return;
}
try {
System.err.println(content);
File f = new File(OUTPUT_FILE);
f.delete();
FileWriter fstream = new FileWriter(OUTPUT_FILE);
BufferedWriter out = new BufferedWriter(fstream);
out.write(content);
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}