diff --git a/application/.classpath b/application/.classpath
index ad0e3de..609aa00 100644
--- a/application/.classpath
+++ b/application/.classpath
@@ -3,6 +3,5 @@
This uses TagSoup to handle real HTML, including all of the brokenness found in the wild.
- */
- public static Spanned fromHtml(String source) {
- return fromHtml(source, null, null);
- }
-
- /**
- * Lazy initialization holder for HTML parser. This class will
- * a) be preloaded by the zygote, or b) not loaded until absolutely
- * necessary.
- */
- private static class HtmlParser {
- private final static HTMLSchema schema = new HTMLSchema();
- }
-
- /**
- * Returns displayable styled text from the provided HTML string.
- * Any <img> tags in the HTML will use the specified ImageGetter
- * to request a representation of the image (use null if you don't
- * want this) and the specified TagHandler to handle unknown tags
- * (specify null if you don't want this).
- *
- * This uses TagSoup to handle real HTML, including all of the brokenness found in the wild.
- */
- public static Spanned fromHtml(String source, ImageGetter imageGetter,
- TagHandler tagHandler) {
- Parser parser = new Parser();
- // Make the font tag restartable
- schema.elementType("font", Schema.M_PCDATA|HTMLModels.M_INLINE, HTMLModels.M_INLINE|HTMLModels.M_NOLINK, Schema.F_RESTART);
- try {
- parser.setProperty(Parser.schemaProperty, schema);
- } catch (org.xml.sax.SAXNotRecognizedException e) {
- // Should not happen.
- throw new RuntimeException(e);
- } catch (org.xml.sax.SAXNotSupportedException e) {
- // Should not happen.
- throw new RuntimeException(e);
- }
-
- HtmlToSpannedConverter converter =
- new HtmlToSpannedConverter(source, imageGetter, tagHandler,
- parser);
- return converter.convert();
- }
-
- /**
- * Returns an HTML representation of the provided Spanned text.
- */
- public static String toHtml(Spanned text) {
- StringBuilder out = new StringBuilder();
- withinHtml(out, text);
- return out.toString();
- }
-
- private static void withinHtml(StringBuilder out, Spanned text) {
- int len = text.length();
-
- int next;
- for (int i = 0; i < text.length(); i = next) {
- next = text.nextSpanTransition(i, len, ParagraphStyle.class);
- ParagraphStyle[] style = text.getSpans(i, next, ParagraphStyle.class);
- String elements = " ";
- boolean needDiv = false;
-
- for(int j = 0; j < style.length; j++) {
- if (style[j] instanceof AlignmentSpan) {
- Layout.Alignment align =
- ((AlignmentSpan) style[j]).getAlignment();
- needDiv = true;
- if (align == Layout.Alignment.ALIGN_CENTER) {
- elements = "align=\"center\" " + elements;
- } else if (align == Layout.Alignment.ALIGN_OPPOSITE) {
- elements = "align=\"right\" " + elements;
- } else {
- elements = "align=\"left\" " + elements;
- }
- }
- }
- if (needDiv) {
- out.append(" ");
-
- int next;
- for (int i = start; i < end; i = next) {
- next = TextUtils.indexOf(text, '\n', i, end);
- if (next < 0) {
- next = end;
- }
-
- int nl = 0;
-
- while (next < end && text.charAt(next) == '\n') {
- nl++;
- next++;
- }
-
- withinParagraph(out, text, i, next - nl, nl, next == end);
- }
-
- out.append(" tags.
- String entext = "
"+TextUtils.htmlEncode(text).replaceAll(" ", " ")+"
";
- String htmltext = Colors.mircColorParser(entext);
- Spanned colortext = Html2.fromHtml(htmltext);
-
- canvas = new SpannableString(prefix + timestamp + nick);
- canvas = new SpannableString(TextUtils.concat(canvas, colortext));
+ canvas = new SpannableString(prefix + timestamp + nick + Colors.mircColorParserSpannable(text));
}
else {
canvas = new SpannableString(prefix + timestamp + nick + Colors.removeStyleAndColors(text));
}
-
if (hasSender()) {
int start = (prefix + timestamp).length() + 1;
int end = start + sender.length();
@@ -259,7 +249,7 @@ public class Message
canvas.setSpan(new ImageSpan(drawable, ImageSpan.ALIGN_BOTTOM), 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
if (hasColor() && settings.showColors()) {
- // Only apply the foreground color on areas that don't already have a foreground color.
+ // Only apply the foreground color to areas that don't already have a foreground color.
ForegroundColorSpan[] spans = canvas.getSpans(0, canvas.length(), ForegroundColorSpan.class);
int start = 0;
for (int i = 0; i < spans.length; i++) {
diff --git a/application/src/org/yaaic/utils/Colors.java b/application/src/org/yaaic/utils/Colors.java
index 5d553a4..00fb05e 100644
--- a/application/src/org/yaaic/utils/Colors.java
+++ b/application/src/org/yaaic/utils/Colors.java
@@ -1,106 +1,141 @@
package org.yaaic.utils;
+import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import android.util.Log;
+import android.graphics.Typeface;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.style.BackgroundColorSpan;
+import android.text.style.CharacterStyle;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.StyleSpan;
+import android.text.style.UnderlineSpan;
public class Colors {
/*
* Colors from the "Classic" theme in mIRC.
*/
- public static final String[] colors = {
- "#FFFFFF", // White
- "#000000", // Black
- "#00007F", // Blue (navy)
- "#009300", // Green
- "#FC0000", // Red
- "#7F0000", // Brown (maroon)
- "#9C009C", // Purple
- "#FC7F00", // Orange (olive)
- "#FFFF00", // Yellow
- "#00FC00", // Light Green (lime)
- "#008080", // Teal (a green/blue cyan)
- "#00FFFF", // Light Cyan (cyan) (aqua)
- "#0000FF", // Light Blue (royal)
- "#FF00FF", // Pink (light purple) (fuchsia)
- "#7F7F7F", // Grey
- "#D2D2D2" // Light Grey (silver)
+ public static final int[] colors = {
+ 0xFFFFFF, // White
+ 0x000000, // Black
+ 0x00007F, // Blue (navy)
+ 0x009300, // Green
+ 0xFC0000, // Red
+ 0x7F0000, // Brown (maroon)
+ 0x9C009C, // Purple
+ 0xFC7F00, // Orange (olive)
+ 0xFFFF00, // Yellow
+ 0x00FC00, // Light Green (lime)
+ 0x008080, // Teal (a green/blue cyan)
+ 0x00FFFF, // Light Cyan (cyan) (aqua)
+ 0x0000FF, // Light Blue (royal)
+ 0xFF00FF, // Pink (light purple) (fuchsia)
+ 0x7F7F7F, // Grey
+ 0xD2D2D2 // Light Grey (silver)
};
- private static final Pattern boldPattern = Pattern.compile("\\x02([^\\x02\\x0F]*)(?:\\x02|(\\x0F))?");
- private static final Pattern underlinePattern = Pattern.compile("\\x1F([^\\x1F\\x0F]*)(?:\\x1F|(\\x0F))?");
- private static final Pattern italicPattern = Pattern.compile("\\x1D([^\\x1D\\x0F]*)(?:\\x1D|(\\x0F))?");
- private static final Pattern inversePattern = Pattern.compile("\\x16([^\\x16\\x0F]*)(?:\\x16|(\\x0F))?");
+ private static final Pattern boldPattern = Pattern.compile("\\x02([^\\x02\\x0F]*)(\\x02|(\\x0F))?");
+ private static final Pattern underlinePattern = Pattern.compile("\\x1F([^\\x1F\\x0F]*)(\\x1F|(\\x0F))?");
+ private static final Pattern italicPattern = Pattern.compile("\\x1D([^\\x1D\\x0F]*)(\\x1D|(\\x0F))?");
+ private static final Pattern inversePattern = Pattern.compile("\\x16([^\\x16\\x0F]*)(\\x16|(\\x0F))?");
private static final Pattern colorPattern = Pattern.compile("\\x03(\\d{1,2})(?:,(\\d{1,2}))?([^\\x03\\x0F]*)(\\x03|\\x0F)?");
private static final Pattern cleanupPattern = Pattern.compile("(?:\\x02|\\x1F|\\x1D|\\x0F|\\x16|\\x03(?:(?:\\d{1,2})(?:,\\d{1,2})?)?)");
private Colors() {}
/**
- * Converts a string with mIRC color codes to a HTML string.
+ * Converts a string with mIRC style and color codes to a SpannableString with
+ * all the style and color codes applied.
*
* @param text A string with mIRC color codes.
- * @return HTML string.
+ * @return A SpannableString with all the styles applied.
*/
- public static String mircColorParser(String text) {
- text = replaceControlCodes(boldPattern.matcher(text), "", "");
- text = replaceControlCodes(underlinePattern.matcher(text), "", "");
- text = replaceControlCodes(italicPattern.matcher(text), "", "");
- // Inverse assumes that the background is black and the foreground is white.
- text = replaceControlCodes(inversePattern.matcher(text), "", "");
+ public static SpannableString mircColorParserSpannable(String text) {
+ SpannableStringBuilder ssb = new SpannableStringBuilder(text);
+ replaceControlCodes(boldPattern.matcher(ssb), ssb, new StyleSpan(Typeface.BOLD));
+ replaceControlCodes(underlinePattern.matcher(ssb), ssb, new UnderlineSpan());
+ replaceControlCodes(italicPattern.matcher(ssb), ssb, new StyleSpan(Typeface.ITALIC));
- StringBuffer sb = new StringBuffer(text);
- StringBuilder ft = new StringBuilder();
- Matcher m = colorPattern.matcher(text);
+ /*
+ * Inverse assumes that the background is black and the foreground is white.
+ * We apply the background color first and then apply the foreground color
+ * to all the parts where BackgroundColorSpans are found.
+ */
+ replaceControlCodes(inversePattern.matcher(ssb), ssb, new BackgroundColorSpan(colors[0] | 0xFF000000));
+ BackgroundColorSpan[] inverseSpans = ssb.getSpans(0, ssb.length(), BackgroundColorSpan.class);
+ for (int i = 0; i < inverseSpans.length; i++) {
+ ssb.setSpan(new ForegroundColorSpan(colors[1] | 0xFF000000), ssb.getSpanStart(inverseSpans[i]),ssb.getSpanEnd(inverseSpans[i]), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ Matcher m = colorPattern.matcher(ssb);
while (m.find()) {
- sb.delete(0, sb.length());
- ft.delete(0, ft.length());
+ int start = m.start();
+ int end = m.end();
- // Build the font tag
- ft.append("= 0) {
- ft.append(" color=\""+colors[color]+"\"");
+ ssb.setSpan(new ForegroundColorSpan(colors[color] | 0xFF000000), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
if (m.group(2) != null) {
color = Integer.parseInt(m.group(2));
if (color <= 15 && color >= 0) {
- ft.append(" bgcolor=\""+colors[color]+"\"");
+ ssb.setSpan(new BackgroundColorSpan(colors[color] | 0xFF000000), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
+ codelength = codelength + m.group(2).length() + 1;
}
- ft.append(">");
- ft.append(m.group(3));
- ft.append("");
- if (m.group(4) != null) {
- ft.append(m.group(4));
- }
- m.appendReplacement(sb, ft.toString());
- m.appendTail(sb);
- m.reset(sb.toString());
+ ssb.delete(start, start+codelength);
+ // Reset the matcher with the modified text so that the ending color code character can be matched again.
+ m.reset(ssb);
}
-
// Remove left over codes
- Log.d("html", removeStyleAndColors(sb.toString()));
- return removeStyleAndColors(sb.toString());
+ return new SpannableString(removeStyleAndColors(ssb));
}
- private static String replaceControlCodes(Matcher m, String startTag, String endTag) {
- /*
- * matcher(...).replaceAll("source
argument is the
- * string from the "src" attribute; the return value should be
- * a Drawable representation of the image or null
- * for a generic replacement image. Make sure you call
- * setBounds() on your Drawable if it doesn't already have
- * its bounds set.
- */
- public Drawable getDrawable(String source);
- }
-
- /**
- * Is notified when HTML tags are encountered that the parser does
- * not know how to interpret.
- */
- public static interface TagHandler {
- /**
- * This method will be called when the HTML parser encounters
- * a tag that it does not know how to interpret.
- */
- public void handleTag(boolean opening, String tag,
- Editable output, XMLReader xmlReader);
- }
-
- private Html2() { }
- private static HTMLSchema schema = new HTMLSchema();
-
- /**
- * Returns displayable styled text from the provided HTML string.
- * Any <img> tags in the HTML will display as a generic
- * replacement image which your program can then go through and
- * replace with real images.
- *
- * ");
- }
-
- withinBlockquote(out, text, i, next);
-
- for (QuoteSpan quote: quotes) {
- out.append("
\n");
- }
- }
- }
-
- private static void withinBlockquote(StringBuilder out, Spanned text,
- int start, int end) {
- out.append("");
- }
- if (style[j] instanceof URLSpan) {
- out.append("");
- }
- if (style[j] instanceof ImageSpan) {
- out.append("");
-
- // Don't output the dummy character underlying the image.
- i = next;
- }
- if (style[j] instanceof AbsoluteSizeSpan) {
- out.append("");
- }
- if (style[j] instanceof ForegroundColorSpan) {
- out.append("");
- }
- if (style[j] instanceof BackgroundColorSpan) {
- out.append("");
- }
- }
-
- withinStyle(out, text, i, next);
-
- for (int j = style.length - 1; j >= 0; j--) {
- if (style[j] instanceof ForegroundColorSpan) {
- out.append("");
- }
- if (style[j] instanceof BackgroundColorSpan) {
- out.append("");
- }
- if (style[j] instanceof AbsoluteSizeSpan) {
- out.append("");
- }
- if (style[j] instanceof URLSpan) {
- out.append("");
- }
- if (style[j] instanceof StrikethroughSpan) {
- out.append("");
- }
- if (style[j] instanceof UnderlineSpan) {
- out.append("");
- }
- if (style[j] instanceof SubscriptSpan) {
- out.append("");
- }
- if (style[j] instanceof SuperscriptSpan) {
- out.append("");
- }
- if (style[j] instanceof TypefaceSpan) {
- String s = ((TypefaceSpan) style[j]).getFamily();
-
- if (s.equals("monospace")) {
- out.append("");
- }
- }
- if (style[j] instanceof StyleSpan) {
- int s = ((StyleSpan) style[j]).getStyle();
-
- if ((s & Typeface.BOLD) != 0) {
- out.append("");
- }
- if ((s & Typeface.ITALIC) != 0) {
- out.append("");
- }
- }
- }
- }
-
- String p = last ? "" : "
";
-
- if (nl == 1) {
- out.append("
\n");
- } else if (nl == 2) {
- out.append(p);
- } else {
- for (int i = 2; i < nl; i++) {
- out.append("
");
- }
-
- out.append(p);
- }
- }
-
- private static void withinStyle(StringBuilder out, Spanned text,
- int start, int end) {
- for (int i = start; i < end; i++) {
- char c = text.charAt(i);
-
- if (c == '<') {
- out.append("<");
- } else if (c == '>') {
- out.append(">");
- } else if (c == '&') {
- out.append("&");
- } else if (c > 0x7E || c < ' ') {
- out.append("" + ((int) c) + ";");
- } else if (c == ' ') {
- while (i + 1 < end && text.charAt(i + 1) == ' ') {
- out.append(" ");
- i++;
- }
-
- out.append(' ');
- } else {
- out.append(c);
- }
- }
- }
-}
-
-class HtmlToSpannedConverter implements ContentHandler {
-
- private static final float[] HEADER_SIZES = {
- 1.5f, 1.4f, 1.3f, 1.2f, 1.1f, 1f,
- };
-
- private final String mSource;
- private final XMLReader mReader;
- private final SpannableStringBuilder mSpannableStringBuilder;
- private final Html2.ImageGetter mImageGetter;
- private final Html2.TagHandler mTagHandler;
-
- public HtmlToSpannedConverter(
- String source, Html2.ImageGetter imageGetter, Html2.TagHandler tagHandler,
- Parser parser) {
- mSource = source;
- mSpannableStringBuilder = new SpannableStringBuilder();
- mImageGetter = imageGetter;
- mTagHandler = tagHandler;
- mReader = parser;
- }
-
- public Spanned convert() {
-
- mReader.setContentHandler(this);
- try {
- mReader.parse(new InputSource(new StringReader(mSource)));
- } catch (IOException e) {
- // We are reading from a string. There should not be IO problems.
- throw new RuntimeException(e);
- } catch (SAXException e) {
- // TagSoup doesn't throw parse exceptions.
- throw new RuntimeException(e);
- }
-
- // Fix flags and range for paragraph-type markup.
- Object[] obj = mSpannableStringBuilder.getSpans(0, mSpannableStringBuilder.length(), ParagraphStyle.class);
- for (int i = 0; i < obj.length; i++) {
- int start = mSpannableStringBuilder.getSpanStart(obj[i]);
- int end = mSpannableStringBuilder.getSpanEnd(obj[i]);
-
- // If the last line of the range is blank, back off by one.
- if (end - 2 >= 0) {
- if (mSpannableStringBuilder.charAt(end - 1) == '\n' &&
- mSpannableStringBuilder.charAt(end - 2) == '\n') {
- end--;
- }
- }
-
- if (end == start) {
- mSpannableStringBuilder.removeSpan(obj[i]);
- } else {
- mSpannableStringBuilder.setSpan(obj[i], start, end, Spannable.SPAN_PARAGRAPH);
- }
- }
-
- return mSpannableStringBuilder;
- }
-
- private void handleStartTag(String tag, Attributes attributes) {
- Log.d("colors", "handleStartTag: "+tag);
- if (tag.equalsIgnoreCase("br")) {
- // We don't need to handle this. TagSoup will ensure that there's a for each
- // so we can safely emite the linebreaks when we handle the close tag.
- } else if (tag.equalsIgnoreCase("p")) {
- handleP(mSpannableStringBuilder);
- } else if (tag.equalsIgnoreCase("div")) {
- handleP(mSpannableStringBuilder);
- } else if (tag.equalsIgnoreCase("em")) {
- start(mSpannableStringBuilder, new Bold());
- } else if (tag.equalsIgnoreCase("b")) {
- start(mSpannableStringBuilder, new Bold());
- } else if (tag.equalsIgnoreCase("strong")) {
- start(mSpannableStringBuilder, new Italic());
- } else if (tag.equalsIgnoreCase("cite")) {
- start(mSpannableStringBuilder, new Italic());
- } else if (tag.equalsIgnoreCase("dfn")) {
- start(mSpannableStringBuilder, new Italic());
- } else if (tag.equalsIgnoreCase("i")) {
- start(mSpannableStringBuilder, new Italic());
- } else if (tag.equalsIgnoreCase("big")) {
- start(mSpannableStringBuilder, new Big());
- } else if (tag.equalsIgnoreCase("small")) {
- start(mSpannableStringBuilder, new Small());
- } else if (tag.equalsIgnoreCase("font")) {
- startFont(mSpannableStringBuilder, attributes);
- } else if (tag.equalsIgnoreCase("blockquote")) {
- handleP(mSpannableStringBuilder);
- start(mSpannableStringBuilder, new Blockquote());
- } else if (tag.equalsIgnoreCase("tt")) {
- start(mSpannableStringBuilder, new Monospace());
- } else if (tag.equalsIgnoreCase("a")) {
- startA(mSpannableStringBuilder, attributes);
- } else if (tag.equalsIgnoreCase("u")) {
- start(mSpannableStringBuilder, new Underline());
- } else if (tag.equalsIgnoreCase("sup")) {
- start(mSpannableStringBuilder, new Super());
- } else if (tag.equalsIgnoreCase("sub")) {
- start(mSpannableStringBuilder, new Sub());
- } else if (tag.length() == 2 &&
- Character.toLowerCase(tag.charAt(0)) == 'h' &&
- tag.charAt(1) >= '1' && tag.charAt(1) <= '6') {
- handleP(mSpannableStringBuilder);
- start(mSpannableStringBuilder, new Header(tag.charAt(1) - '1'));
- } else if (tag.equalsIgnoreCase("img")) {
- startImg(mSpannableStringBuilder, attributes, mImageGetter);
- } else if (mTagHandler != null) {
- mTagHandler.handleTag(true, tag, mSpannableStringBuilder, mReader);
- }
- }
-
- private void handleEndTag(String tag) {
- Log.d("colors", "handleEndTag: "+tag);
- if (tag.equalsIgnoreCase("br")) {
- handleBr(mSpannableStringBuilder);
- } else if (tag.equalsIgnoreCase("p")) {
- handleP(mSpannableStringBuilder);
- } else if (tag.equalsIgnoreCase("div")) {
- handleP(mSpannableStringBuilder);
- } else if (tag.equalsIgnoreCase("em")) {
- end(mSpannableStringBuilder, Bold.class, new StyleSpan(Typeface.BOLD));
- } else if (tag.equalsIgnoreCase("b")) {
- end(mSpannableStringBuilder, Bold.class, new StyleSpan(Typeface.BOLD));
- } else if (tag.equalsIgnoreCase("strong")) {
- end(mSpannableStringBuilder, Italic.class, new StyleSpan(Typeface.ITALIC));
- } else if (tag.equalsIgnoreCase("cite")) {
- end(mSpannableStringBuilder, Italic.class, new StyleSpan(Typeface.ITALIC));
- } else if (tag.equalsIgnoreCase("dfn")) {
- end(mSpannableStringBuilder, Italic.class, new StyleSpan(Typeface.ITALIC));
- } else if (tag.equalsIgnoreCase("i")) {
- end(mSpannableStringBuilder, Italic.class, new StyleSpan(Typeface.ITALIC));
- } else if (tag.equalsIgnoreCase("big")) {
- end(mSpannableStringBuilder, Big.class, new RelativeSizeSpan(1.25f));
- } else if (tag.equalsIgnoreCase("small")) {
- end(mSpannableStringBuilder, Small.class, new RelativeSizeSpan(0.8f));
- } else if (tag.equalsIgnoreCase("font")) {
- endFont(mSpannableStringBuilder);
- } else if (tag.equalsIgnoreCase("blockquote")) {
- handleP(mSpannableStringBuilder);
- end(mSpannableStringBuilder, Blockquote.class, new QuoteSpan());
- } else if (tag.equalsIgnoreCase("tt")) {
- end(mSpannableStringBuilder, Monospace.class,
- new TypefaceSpan("monospace"));
- } else if (tag.equalsIgnoreCase("a")) {
- endA(mSpannableStringBuilder);
- } else if (tag.equalsIgnoreCase("u")) {
- end(mSpannableStringBuilder, Underline.class, new UnderlineSpan());
- } else if (tag.equalsIgnoreCase("sup")) {
- end(mSpannableStringBuilder, Super.class, new SuperscriptSpan());
- } else if (tag.equalsIgnoreCase("sub")) {
- end(mSpannableStringBuilder, Sub.class, new SubscriptSpan());
- } else if (tag.length() == 2 &&
- Character.toLowerCase(tag.charAt(0)) == 'h' &&
- tag.charAt(1) >= '1' && tag.charAt(1) <= '6') {
- handleP(mSpannableStringBuilder);
- endHeader(mSpannableStringBuilder);
- } else if (mTagHandler != null) {
- mTagHandler.handleTag(false, tag, mSpannableStringBuilder, mReader);
- }
- }
-
- private static void handleP(SpannableStringBuilder text) {
- int len = text.length();
-
- if (len >= 1 && text.charAt(len - 1) == '\n') {
- if (len >= 2 && text.charAt(len - 2) == '\n') {
- return;
- }
-
- text.append("\n");
- return;
- }
-
- if (len != 0) {
- text.append("\n\n");
- }
- }
-
- private static void handleBr(SpannableStringBuilder text) {
- text.append("\n");
- }
-
- private static Object getLast(Spanned text, Class kind) {
- /*
- * This knows that the last returned object from getSpans()
- * will be the most recently added.
- */
- Object[] objs = text.getSpans(0, text.length(), kind);
-
- if (objs.length == 0) {
- return null;
- } else {
- return objs[objs.length - 1];
- }
- }
-
- private static void start(SpannableStringBuilder text, Object mark) {
- int len = text.length();
- text.setSpan(mark, len, len, Spannable.SPAN_MARK_MARK);
- }
-
- private static void end(SpannableStringBuilder text, Class kind,
- Object repl) {
- int len = text.length();
- Object obj = getLast(text, kind);
- int where = text.getSpanStart(obj);
-
- text.removeSpan(obj);
-
- if (where != len) {
- text.setSpan(repl, where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
-
- return;
- }
-
- private static void startImg(SpannableStringBuilder text,
- Attributes attributes, Html2.ImageGetter img) {
- String src = attributes.getValue("", "src");
- Drawable d = null;
-
- if (img != null) {
- d = img.getDrawable(src);
- }
-
- if (d == null) {
- d = Resources.getSystem().
- getDrawable(R.drawable.unknown_image);
- d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
- }
-
- int len = text.length();
- text.append("\uFFFC");
-
- text.setSpan(new ImageSpan(d, src), len, text.length(),
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
-
- private static void startFont(SpannableStringBuilder text,
- Attributes attributes) {
- String color = attributes.getValue("", "color");
- String face = attributes.getValue("", "face");
- String bgColor = attributes.getValue("", "bgcolor");
-
- int len = text.length();
- text.setSpan(new Font(color, face, bgColor), len, len, Spannable.SPAN_MARK_MARK);
- }
-
- private static void endFont(SpannableStringBuilder text) {
- int len = text.length();
- Object obj = getLast(text, Font.class);
- int where = text.getSpanStart(obj);
-
- text.removeSpan(obj);
-
- if (where != len) {
- Font f = (Font) obj;
-
- if (!TextUtils.isEmpty(f.mColor)) {
- if (f.mColor.startsWith("@")) {
- Resources res = Resources.getSystem();
- String name = f.mColor.substring(1);
- int colorRes = res.getIdentifier(name, "color", "android");
- if (colorRes != 0) {
- ColorStateList colors = res.getColorStateList(colorRes);
- text.setSpan(new TextAppearanceSpan(null, 0, 0, colors, null),
- where, len,
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- } else {
- int c = getHtmlColor(f.mColor);
- if (c != -1) {
- text.setSpan(new ForegroundColorSpan(c | 0xFF000000),
- where, len,
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- }
- }
-
- if (!TextUtils.isEmpty(f.mBgColor)) {
- if (f.mBgColor.startsWith("@")) {
- Resources res = Resources.getSystem();
- String name = f.mBgColor.substring(1);
- int colorRes = res.getIdentifier(name, "color", "android");
- if (colorRes != 0) {
- ColorStateList colors = res.getColorStateList(colorRes);
- text.setSpan(new BackgroundColorSpan(colors.getDefaultColor()),
- where, len,
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- } else {
- int c = getHtmlColor(f.mBgColor);
- if (c != -1) {
- text.setSpan(new BackgroundColorSpan(c | 0xFF000000),
- where, len,
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- }
- }
-
- if (f.mFace != null) {
- text.setSpan(new TypefaceSpan(f.mFace), where, len,
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- }
- }
-
- private static void startA(SpannableStringBuilder text, Attributes attributes) {
- String href = attributes.getValue("", "href");
-
- int len = text.length();
- text.setSpan(new Href(href), len, len, Spannable.SPAN_MARK_MARK);
- }
-
- private static void endA(SpannableStringBuilder text) {
- int len = text.length();
- Object obj = getLast(text, Href.class);
- int where = text.getSpanStart(obj);
-
- text.removeSpan(obj);
-
- if (where != len) {
- Href h = (Href) obj;
-
- if (h.mHref != null) {
- text.setSpan(new URLSpan(h.mHref), where, len,
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- }
- }
-
- private static void endHeader(SpannableStringBuilder text) {
- int len = text.length();
- Object obj = getLast(text, Header.class);
-
- int where = text.getSpanStart(obj);
-
- text.removeSpan(obj);
-
- // Back off not to change only the text, not the blank line.
- while (len > where && text.charAt(len - 1) == '\n') {
- len--;
- }
-
- if (where != len) {
- Header h = (Header) obj;
-
- text.setSpan(new RelativeSizeSpan(HEADER_SIZES[h.mLevel]),
- where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- text.setSpan(new StyleSpan(Typeface.BOLD),
- where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- }
-
- @Override
- public void setDocumentLocator(Locator locator) {
- }
-
- @Override
- public void startDocument() throws SAXException {
- }
-
- @Override
- public void endDocument() throws SAXException {
- }
-
- @Override
- public void startPrefixMapping(String prefix, String uri) throws SAXException {
- }
-
- @Override
- public void endPrefixMapping(String prefix) throws SAXException {
- }
-
- @Override
- public void startElement(String uri, String localName, String qName, Attributes attributes)
- throws SAXException {
- handleStartTag(localName, attributes);
- }
-
- @Override
- public void endElement(String uri, String localName, String qName) throws SAXException {
- handleEndTag(localName);
- }
-
- @Override
- public void characters(char ch[], int start, int length) throws SAXException {
- StringBuilder sb = new StringBuilder();
-
- /*
- * Ignore whitespace that immediately follows other whitespace;
- * newlines count as spaces.
- */
-
- for (int i = 0; i < length; i++) {
- char c = ch[i + start];
-
- if (c == ' ' || c == '\n') {
- char pred;
- int len = sb.length();
-
- if (len == 0) {
- len = mSpannableStringBuilder.length();
-
- if (len == 0) {
- pred = '\n';
- } else {
- pred = mSpannableStringBuilder.charAt(len - 1);
- }
- } else {
- pred = sb.charAt(len - 1);
- }
-
- if (pred != ' ' && pred != '\n') {
- sb.append(' ');
- }
- } else {
- sb.append(c);
- }
- }
-
- mSpannableStringBuilder.append(sb);
- }
-
- @Override
- public void ignorableWhitespace(char ch[], int start, int length) throws SAXException {
- }
-
- @Override
- public void processingInstruction(String target, String data) throws SAXException {
- }
-
- @Override
- public void skippedEntity(String name) throws SAXException {
- }
-
- private static class Bold { }
- private static class Italic { }
- private static class Underline { }
- private static class Big { }
- private static class Small { }
- private static class Monospace { }
- private static class Blockquote { }
- private static class Super { }
- private static class Sub { }
-
- private static class Font {
- public String mColor;
- public String mBgColor;
- public String mFace;
-
- public Font(String color, String face, String bgColor) {
- mColor = color;
- mFace = face;
- mBgColor = bgColor;
- }
- }
-
- private static class Href {
- public String mHref;
-
- public Href(String href) {
- mHref = href;
- }
- }
-
- private static class Header {
- private final int mLevel;
-
- public Header(int level) {
- mLevel = level;
- }
- }
-
- private static HashMap