Html.java revision 94d5e9ad6533864f098b99c231d69c77230a828f
19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2007 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.text;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport org.ccil.cowan.tagsoup.HTMLSchema;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport org.ccil.cowan.tagsoup.Parser;
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport org.xml.sax.Attributes;
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport org.xml.sax.ContentHandler;
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport org.xml.sax.InputSource;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport org.xml.sax.Locator;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport org.xml.sax.SAXException;
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport org.xml.sax.XMLReader;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
289cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringertimport android.content.res.ColorStateList;
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.Resources;
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Typeface;
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.drawable.Drawable;
32105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Projectimport android.text.style.AbsoluteSizeSpan;
33105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Projectimport android.text.style.AlignmentSpan;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.CharacterStyle;
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.ForegroundColorSpan;
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.ImageSpan;
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.ParagraphStyle;
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.QuoteSpan;
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.RelativeSizeSpan;
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.StrikethroughSpan;
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.StyleSpan;
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.SubscriptSpan;
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.SuperscriptSpan;
449cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringertimport android.text.style.TextAppearanceSpan;
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.TypefaceSpan;
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.URLSpan;
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.UnderlineSpan;
48105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Projectimport android.util.Log;
492269d1572e5fcfb725ea55f5764d8c3280d69f6dDianne Hackborn
502269d1572e5fcfb725ea55f5764d8c3280d69f6dDianne Hackbornimport com.android.internal.util.XmlUtils;
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.IOException;
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.StringReader;
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.nio.CharBuffer;
559cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringertimport java.util.HashMap;
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * This class processes HTML strings into displayable styled text.
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Not all HTML tags are supported.
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class Html {
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Retrieves images for HTML <img> tags.
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static interface ImageGetter {
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * This methos is called when the HTML parser encounters an
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * &lt;img&gt; tag.  The <code>source</code> argument is the
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * string from the "src" attribute; the return value should be
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * a Drawable representation of the image or <code>null</code>
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * for a generic replacement image.  Make sure you call
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * setBounds() on your Drawable if it doesn't already have
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * its bounds set.
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Drawable getDrawable(String source);
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Is notified when HTML tags are encountered that the parser does
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * not know how to interpret.
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static interface TagHandler {
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * This method will be called whenn the HTML parser encounters
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * a tag that it does not know how to interpret.
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void handleTag(boolean opening, String tag,
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                 Editable output, XMLReader xmlReader);
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Html() { }
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns displayable styled text from the provided HTML string.
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Any &lt;img&gt; tags in the HTML will display as a generic
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * replacement image which your program can then go through and
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * replace with real images.
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <p>This uses TagSoup to handle real HTML, including all of the brokenness found in the wild.
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static Spanned fromHtml(String source) {
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return fromHtml(source, null, null);
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Lazy initialization holder for HTML parser. This class will
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * a) be preloaded by the zygote, or b) not loaded until absolutely
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * necessary.
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static class HtmlParser {
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private static final HTMLSchema schema = new HTMLSchema();
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns displayable styled text from the provided HTML string.
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Any &lt;img&gt; tags in the HTML will use the specified ImageGetter
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * to request a representation of the image (use null if you don't
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * want this) and the specified TagHandler to handle unknown tags
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * (specify null if you don't want this).
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <p>This uses TagSoup to handle real HTML, including all of the brokenness found in the wild.
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static Spanned fromHtml(String source, ImageGetter imageGetter,
1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                   TagHandler tagHandler) {
1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Parser parser = new Parser();
1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            parser.setProperty(Parser.schemaProperty, HtmlParser.schema);
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (org.xml.sax.SAXNotRecognizedException e) {
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Should not happen.
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new RuntimeException(e);
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (org.xml.sax.SAXNotSupportedException e) {
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Should not happen.
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new RuntimeException(e);
1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        HtmlToSpannedConverter converter =
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                new HtmlToSpannedConverter(source, imageGetter, tagHandler,
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        parser);
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return converter.convert();
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns an HTML representation of the provided Spanned text.
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static String toHtml(Spanned text) {
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        StringBuilder out = new StringBuilder();
147105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        withinHtml(out, text);
148105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        return out.toString();
149105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    }
150105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
151105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    private static void withinHtml(StringBuilder out, Spanned text) {
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int len = text.length();
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int next;
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < text.length(); i = next) {
156105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            next = text.nextSpanTransition(i, len, ParagraphStyle.class);
157105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            ParagraphStyle[] style = text.getSpans(i, next, ParagraphStyle.class);
15832048300e917c9181927ac017d02855bbde940efSatoshi Kataoka            String elements = " ";
15900ba76670fd06d9c51cce36a74c384a212f705b5Eric Fischer            boolean needDiv = false;
16000ba76670fd06d9c51cce36a74c384a212f705b5Eric Fischer
161105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            for(int j = 0; j < style.length; j++) {
162105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                if (style[j] instanceof AlignmentSpan) {
163105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    Layout.Alignment align =
164105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                        ((AlignmentSpan) style[j]).getAlignment();
16500ba76670fd06d9c51cce36a74c384a212f705b5Eric Fischer                    needDiv = true;
166105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    if (align == Layout.Alignment.ALIGN_CENTER) {
16732048300e917c9181927ac017d02855bbde940efSatoshi Kataoka                        elements = "align=\"center\" " + elements;
168105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    } else if (align == Layout.Alignment.ALIGN_OPPOSITE) {
16932048300e917c9181927ac017d02855bbde940efSatoshi Kataoka                        elements = "align=\"right\" " + elements;
170105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    } else {
17132048300e917c9181927ac017d02855bbde940efSatoshi Kataoka                        elements = "align=\"left\" " + elements;
172105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    }
173105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                }
174105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            }
17500ba76670fd06d9c51cce36a74c384a212f705b5Eric Fischer            if (needDiv) {
17632048300e917c9181927ac017d02855bbde940efSatoshi Kataoka                out.append("<div " + elements + ">");
177105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            }
178105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
179105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            withinDiv(out, text, i, next);
180105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
18100ba76670fd06d9c51cce36a74c384a212f705b5Eric Fischer            if (needDiv) {
182105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                out.append("</div>");
183105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            }
184105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        }
185105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    }
186105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
187105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    private static void withinDiv(StringBuilder out, Spanned text,
188105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            int start, int end) {
189105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        int next;
190105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        for (int i = start; i < end; i = next) {
191105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            next = text.nextSpanTransition(i, end, QuoteSpan.class);
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            QuoteSpan[] quotes = text.getSpans(i, next, QuoteSpan.class);
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (QuoteSpan quote: quotes) {
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.append("<blockquote>");
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            withinBlockquote(out, text, i, next);
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (QuoteSpan quote: quotes) {
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.append("</blockquote>\n");
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void withinBlockquote(StringBuilder out, Spanned text,
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                         int start, int end) {
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        out.append("<p>");
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int next;
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = start; i < end; i = next) {
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            next = TextUtils.indexOf(text, '\n', i, end);
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (next < 0) {
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                next = end;
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int nl = 0;
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            while (next < end && text.charAt(next) == '\n') {
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                nl++;
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                next++;
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            withinParagraph(out, text, i, next - nl, nl, next == end);
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        out.append("</p>\n");
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void withinParagraph(StringBuilder out, Spanned text,
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        int start, int end, int nl,
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        boolean last) {
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int next;
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = start; i < end; i = next) {
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            next = text.nextSpanTransition(i, end, CharacterStyle.class);
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            CharacterStyle[] style = text.getSpans(i, next,
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                                   CharacterStyle.class);
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int j = 0; j < style.length; j++) {
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (style[j] instanceof StyleSpan) {
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    int s = ((StyleSpan) style[j]).getStyle();
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if ((s & Typeface.BOLD) != 0) {
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        out.append("<b>");
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if ((s & Typeface.ITALIC) != 0) {
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        out.append("<i>");
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (style[j] instanceof TypefaceSpan) {
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    String s = ((TypefaceSpan) style[j]).getFamily();
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (s.equals("monospace")) {
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        out.append("<tt>");
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (style[j] instanceof SuperscriptSpan) {
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    out.append("<sup>");
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (style[j] instanceof SubscriptSpan) {
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    out.append("<sub>");
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (style[j] instanceof UnderlineSpan) {
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    out.append("<u>");
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (style[j] instanceof StrikethroughSpan) {
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    out.append("<strike>");
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (style[j] instanceof URLSpan) {
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    out.append("<a href=\"");
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    out.append(((URLSpan) style[j]).getURL());
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    out.append("\">");
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (style[j] instanceof ImageSpan) {
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    out.append("<img src=\"");
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    out.append(((ImageSpan) style[j]).getSource());
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    out.append("\">");
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // Don't output the dummy character underlying the image.
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    i = next;
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
282105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                if (style[j] instanceof AbsoluteSizeSpan) {
283105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    out.append("<font size =\"");
284105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    out.append(((AbsoluteSizeSpan) style[j]).getSize() / 6);
285105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    out.append("\">");
286105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                }
287105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                if (style[j] instanceof ForegroundColorSpan) {
288105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    out.append("<font color =\"#");
289105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    String color = Integer.toHexString(((ForegroundColorSpan)
290105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                            style[j]).getForegroundColor() + 0x01000000);
291105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    while (color.length() < 6) {
292105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                        color = "0" + color;
293105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    }
294105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    out.append(color);
295105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    out.append("\">");
296105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                }
2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            withinStyle(out, text, i, next);
3009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int j = style.length - 1; j >= 0; j--) {
302105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                if (style[j] instanceof ForegroundColorSpan) {
303105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    out.append("</font>");
304105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                }
305105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                if (style[j] instanceof AbsoluteSizeSpan) {
306105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    out.append("</font>");
307105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                }
3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (style[j] instanceof URLSpan) {
3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    out.append("</a>");
3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (style[j] instanceof StrikethroughSpan) {
3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    out.append("</strike>");
3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (style[j] instanceof UnderlineSpan) {
3159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    out.append("</u>");
3169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (style[j] instanceof SubscriptSpan) {
3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    out.append("</sub>");
3199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (style[j] instanceof SuperscriptSpan) {
3219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    out.append("</sup>");
3229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (style[j] instanceof TypefaceSpan) {
3249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    String s = ((TypefaceSpan) style[j]).getFamily();
3259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (s.equals("monospace")) {
3279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        out.append("</tt>");
3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
3299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (style[j] instanceof StyleSpan) {
3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    int s = ((StyleSpan) style[j]).getStyle();
3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if ((s & Typeface.BOLD) != 0) {
3349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        out.append("</b>");
3359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
3369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if ((s & Typeface.ITALIC) != 0) {
3379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        out.append("</i>");
3389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
3399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String p = last ? "" : "</p>\n<p>";
3449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (nl == 1) {
3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.append("<br>\n");
3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (nl == 2) {
3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.append(p);
3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
3509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 2; i < nl; i++) {
3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.append("<br>");
3529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.append(p);
3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void withinStyle(StringBuilder out, Spanned text,
3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    int start, int end) {
3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = start; i < end; i++) {
3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char c = text.charAt(i);
3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c == '<') {
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.append("&lt;");
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (c == '>') {
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.append("&gt;");
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (c == '&') {
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.append("&amp;");
3699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (c > 0x7E || c < ' ') {
3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.append("&#" + ((int) c) + ";");
3719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (c == ' ') {
3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                while (i + 1 < end && text.charAt(i + 1) == ' ') {
3739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    out.append("&nbsp;");
3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    i++;
3759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
3769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.append(' ');
3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
3799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.append(c);
3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectclass HtmlToSpannedConverter implements ContentHandler {
3869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final float[] HEADER_SIZES = {
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        1.5f, 1.4f, 1.3f, 1.2f, 1.1f, 1f,
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
3909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private String mSource;
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private XMLReader mReader;
3939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private SpannableStringBuilder mSpannableStringBuilder;
3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Html.ImageGetter mImageGetter;
3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Html.TagHandler mTagHandler;
3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public HtmlToSpannedConverter(
3989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String source, Html.ImageGetter imageGetter, Html.TagHandler tagHandler,
3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Parser parser) {
4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSource = source;
4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSpannableStringBuilder = new SpannableStringBuilder();
4029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mImageGetter = imageGetter;
4039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mTagHandler = tagHandler;
4049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mReader = parser;
4059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public Spanned convert() {
4089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mReader.setContentHandler(this);
4109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
4119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mReader.parse(new InputSource(new StringReader(mSource)));
4129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
4139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // We are reading from a string. There should not be IO problems.
4149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new RuntimeException(e);
4159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (SAXException e) {
4169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // TagSoup doesn't throw parse exceptions.
4179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new RuntimeException(e);
4189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Fix flags and range for paragraph-type markup.
4219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Object[] obj = mSpannableStringBuilder.getSpans(0, mSpannableStringBuilder.length(), ParagraphStyle.class);
4229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < obj.length; i++) {
4239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int start = mSpannableStringBuilder.getSpanStart(obj[i]);
4249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int end = mSpannableStringBuilder.getSpanEnd(obj[i]);
4259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // If the last line of the range is blank, back off by one.
4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (end - 2 >= 0) {
4289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (mSpannableStringBuilder.charAt(end - 1) == '\n' &&
4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mSpannableStringBuilder.charAt(end - 2) == '\n') {
4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    end--;
4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
4329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (end == start) {
4359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mSpannableStringBuilder.removeSpan(obj[i]);
4369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
4379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mSpannableStringBuilder.setSpan(obj[i], start, end, Spannable.SPAN_PARAGRAPH);
4389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mSpannableStringBuilder;
4429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void handleStartTag(String tag, Attributes attributes) {
4459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (tag.equalsIgnoreCase("br")) {
4469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // We don't need to handle this. TagSoup will ensure that there's a </br> for each <br>
4479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // so we can safely emite the linebreaks when we handle the close tag.
4489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("p")) {
4499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            handleP(mSpannableStringBuilder);
4509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("div")) {
4519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            handleP(mSpannableStringBuilder);
45294d5e9ad6533864f098b99c231d69c77230a828fRomain Guy        } else if (tag.equalsIgnoreCase("strong")) {
4539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            start(mSpannableStringBuilder, new Bold());
4549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("b")) {
4559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            start(mSpannableStringBuilder, new Bold());
45694d5e9ad6533864f098b99c231d69c77230a828fRomain Guy        } else if (tag.equalsIgnoreCase("em")) {
4579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            start(mSpannableStringBuilder, new Italic());
4589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("cite")) {
4599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            start(mSpannableStringBuilder, new Italic());
4609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("dfn")) {
4619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            start(mSpannableStringBuilder, new Italic());
4629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("i")) {
4639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            start(mSpannableStringBuilder, new Italic());
4649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("big")) {
4659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            start(mSpannableStringBuilder, new Big());
4669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("small")) {
4679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            start(mSpannableStringBuilder, new Small());
4689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("font")) {
4699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            startFont(mSpannableStringBuilder, attributes);
4709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("blockquote")) {
4719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            handleP(mSpannableStringBuilder);
4729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            start(mSpannableStringBuilder, new Blockquote());
4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("tt")) {
4749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            start(mSpannableStringBuilder, new Monospace());
4759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("a")) {
4769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            startA(mSpannableStringBuilder, attributes);
4779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("u")) {
4789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            start(mSpannableStringBuilder, new Underline());
4799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("sup")) {
4809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            start(mSpannableStringBuilder, new Super());
4819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("sub")) {
4829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            start(mSpannableStringBuilder, new Sub());
4839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.length() == 2 &&
4849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                   Character.toLowerCase(tag.charAt(0)) == 'h' &&
4859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                   tag.charAt(1) >= '1' && tag.charAt(1) <= '6') {
4869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            handleP(mSpannableStringBuilder);
4879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            start(mSpannableStringBuilder, new Header(tag.charAt(1) - '1'));
4889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("img")) {
4899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            startImg(mSpannableStringBuilder, attributes, mImageGetter);
4909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (mTagHandler != null) {
4919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mTagHandler.handleTag(true, tag, mSpannableStringBuilder, mReader);
4929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void handleEndTag(String tag) {
4969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (tag.equalsIgnoreCase("br")) {
4979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            handleBr(mSpannableStringBuilder);
4989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("p")) {
4999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            handleP(mSpannableStringBuilder);
5009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("div")) {
5019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            handleP(mSpannableStringBuilder);
5029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("em")) {
5039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            end(mSpannableStringBuilder, Bold.class, new StyleSpan(Typeface.BOLD));
5049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("b")) {
5059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            end(mSpannableStringBuilder, Bold.class, new StyleSpan(Typeface.BOLD));
5069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("strong")) {
5079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            end(mSpannableStringBuilder, Italic.class, new StyleSpan(Typeface.ITALIC));
5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("cite")) {
5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            end(mSpannableStringBuilder, Italic.class, new StyleSpan(Typeface.ITALIC));
5109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("dfn")) {
5119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            end(mSpannableStringBuilder, Italic.class, new StyleSpan(Typeface.ITALIC));
5129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("i")) {
5139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            end(mSpannableStringBuilder, Italic.class, new StyleSpan(Typeface.ITALIC));
5149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("big")) {
5159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            end(mSpannableStringBuilder, Big.class, new RelativeSizeSpan(1.25f));
5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("small")) {
5179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            end(mSpannableStringBuilder, Small.class, new RelativeSizeSpan(0.8f));
5189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("font")) {
5199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            endFont(mSpannableStringBuilder);
5209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("blockquote")) {
5219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            handleP(mSpannableStringBuilder);
5229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            end(mSpannableStringBuilder, Blockquote.class, new QuoteSpan());
5239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("tt")) {
5249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            end(mSpannableStringBuilder, Monospace.class,
5259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    new TypefaceSpan("monospace"));
5269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("a")) {
5279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            endA(mSpannableStringBuilder);
5289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("u")) {
5299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            end(mSpannableStringBuilder, Underline.class, new UnderlineSpan());
5309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("sup")) {
5319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            end(mSpannableStringBuilder, Super.class, new SuperscriptSpan());
5329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.equalsIgnoreCase("sub")) {
5339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            end(mSpannableStringBuilder, Sub.class, new SubscriptSpan());
5349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (tag.length() == 2 &&
5359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Character.toLowerCase(tag.charAt(0)) == 'h' &&
5369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                tag.charAt(1) >= '1' && tag.charAt(1) <= '6') {
5379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            handleP(mSpannableStringBuilder);
5389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            endHeader(mSpannableStringBuilder);
5399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (mTagHandler != null) {
5409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mTagHandler.handleTag(false, tag, mSpannableStringBuilder, mReader);
5419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void handleP(SpannableStringBuilder text) {
5459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int len = text.length();
5469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (len >= 1 && text.charAt(len - 1) == '\n') {
5489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (len >= 2 && text.charAt(len - 2) == '\n') {
5499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return;
5509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            text.append("\n");
5539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
5549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (len != 0) {
5579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            text.append("\n\n");
5589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void handleBr(SpannableStringBuilder text) {
5629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        text.append("\n");
5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static Object getLast(Spanned text, Class kind) {
5669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /*
5679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * This knows that the last returned object from getSpans()
5689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * will be the most recently added.
5699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
5709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Object[] objs = text.getSpans(0, text.length(), kind);
5719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (objs.length == 0) {
5739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
5749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
5759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return objs[objs.length - 1];
5769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void start(SpannableStringBuilder text, Object mark) {
5809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int len = text.length();
5819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        text.setSpan(mark, len, len, Spannable.SPAN_MARK_MARK);
5829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void end(SpannableStringBuilder text, Class kind,
5859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            Object repl) {
5869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int len = text.length();
5879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Object obj = getLast(text, kind);
5889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int where = text.getSpanStart(obj);
5899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        text.removeSpan(obj);
5919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (where != len) {
5939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            text.setSpan(repl, where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
5949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return;
5979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void startImg(SpannableStringBuilder text,
6009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                 Attributes attributes, Html.ImageGetter img) {
6019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String src = attributes.getValue("", "src");
6029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Drawable d = null;
6039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (img != null) {
6059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            d = img.getDrawable(src);
6069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (d == null) {
6099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            d = Resources.getSystem().
6109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    getDrawable(com.android.internal.R.drawable.unknown_image);
6119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
6129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int len = text.length();
6159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        text.append("\uFFFC");
6169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        text.setSpan(new ImageSpan(d, src), len, text.length(),
6189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                     Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
6199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void startFont(SpannableStringBuilder text,
6229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                  Attributes attributes) {
6239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String color = attributes.getValue("", "color");
6249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String face = attributes.getValue("", "face");
6259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int len = text.length();
6279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        text.setSpan(new Font(color, face), len, len, Spannable.SPAN_MARK_MARK);
6289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void endFont(SpannableStringBuilder text) {
6319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int len = text.length();
6329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Object obj = getLast(text, Font.class);
6339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int where = text.getSpanStart(obj);
6349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        text.removeSpan(obj);
6369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (where != len) {
6389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Font f = (Font) obj;
6399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6409cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert            if (!TextUtils.isEmpty(f.mColor)) {
6419cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert                if (f.mColor.startsWith("@")) {
6429cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert                    Resources res = Resources.getSystem();
6439cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert                    String name = f.mColor.substring(1);
6449cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert                    int colorRes = res.getIdentifier(name, "color", "android");
6459cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert                    if (colorRes != 0) {
6469cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert                        ColorStateList colors = res.getColorStateList(colorRes);
6479cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert                        text.setSpan(new TextAppearanceSpan(null, 0, 0, colors, null),
6489cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert                                where, len,
6499cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
6509cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert                    }
6519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
6529cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert                    int c = getHtmlColor(f.mColor);
6539cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert                    if (c != -1) {
6549cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert                        text.setSpan(new ForegroundColorSpan(c | 0xFF000000),
6559cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert                                where, len,
6569cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
6579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
6589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
6599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (f.mFace != null) {
6629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                text.setSpan(new TypefaceSpan(f.mFace), where, len,
6639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                             Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
6649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void startA(SpannableStringBuilder text, Attributes attributes) {
6699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String href = attributes.getValue("", "href");
6709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int len = text.length();
6729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        text.setSpan(new Href(href), len, len, Spannable.SPAN_MARK_MARK);
6739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void endA(SpannableStringBuilder text) {
6769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int len = text.length();
6779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Object obj = getLast(text, Href.class);
6789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int where = text.getSpanStart(obj);
6799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        text.removeSpan(obj);
6819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (where != len) {
6839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Href h = (Href) obj;
6849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (h.mHref != null) {
6869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                text.setSpan(new URLSpan(h.mHref), where, len,
6879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                             Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
6889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void endHeader(SpannableStringBuilder text) {
6939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int len = text.length();
6949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Object obj = getLast(text, Header.class);
6959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int where = text.getSpanStart(obj);
6979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        text.removeSpan(obj);
6999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Back off not to change only the text, not the blank line.
7019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (len > where && text.charAt(len - 1) == '\n') {
7029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            len--;
7039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (where != len) {
7069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Header h = (Header) obj;
7079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            text.setSpan(new RelativeSizeSpan(HEADER_SIZES[h.mLevel]),
7099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                         where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
7109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            text.setSpan(new StyleSpan(Typeface.BOLD),
7119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                         where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
7129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void setDocumentLocator(Locator locator) {
7169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void startDocument() throws SAXException {
7199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void endDocument() throws SAXException {
7229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void startPrefixMapping(String prefix, String uri) throws SAXException {
7259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void endPrefixMapping(String prefix) throws SAXException {
7289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void startElement(String uri, String localName, String qName, Attributes attributes)
7319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throws SAXException {
7329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        handleStartTag(localName, attributes);
7339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void endElement(String uri, String localName, String qName) throws SAXException {
7369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        handleEndTag(localName);
7379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void characters(char ch[], int start, int length) throws SAXException {
7409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        StringBuilder sb = new StringBuilder();
7419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /*
7439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Ignore whitespace that immediately follows other whitespace;
7449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * newlines count as spaces.
7459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
7469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < length; i++) {
7489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char c = ch[i + start];
7499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c == ' ' || c == '\n') {
7519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                char pred;
7529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int len = sb.length();
7539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (len == 0) {
7559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    len = mSpannableStringBuilder.length();
7569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (len == 0) {
7589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        pred = '\n';
7599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
7609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        pred = mSpannableStringBuilder.charAt(len - 1);
7619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
7629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
7639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    pred = sb.charAt(len - 1);
7649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
7659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (pred != ' ' && pred != '\n') {
7679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append(' ');
7689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
7699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
7709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append(c);
7719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
7729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mSpannableStringBuilder.append(sb);
7759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void ignorableWhitespace(char ch[], int start, int length) throws SAXException {
7789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void processingInstruction(String target, String data) throws SAXException {
7819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void skippedEntity(String name) throws SAXException {
7849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static class Bold { }
7879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static class Italic { }
7889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static class Underline { }
7899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static class Big { }
7909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static class Small { }
7919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static class Monospace { }
7929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static class Blockquote { }
7939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static class Super { }
7949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static class Sub { }
7959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static class Font {
7979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String mColor;
7989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String mFace;
7999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Font(String color, String face) {
8019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mColor = color;
8029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mFace = face;
8039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static class Href {
8079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String mHref;
8089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Href(String href) {
8109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mHref = href;
8119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static class Header {
8159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private int mLevel;
8169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public Header(int level) {
8189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mLevel = level;
8199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8219cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert
8229cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert    private static HashMap<String,Integer> COLORS = buildColorMap();
8239cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert
8249cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert    private static HashMap<String,Integer> buildColorMap() {
8259cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert        HashMap<String,Integer> map = new HashMap<String,Integer>();
8269cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert        map.put("aqua", 0x00FFFF);
8279cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert        map.put("black", 0x000000);
8289cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert        map.put("blue", 0x0000FF);
8299cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert        map.put("fuchsia", 0xFF00FF);
8309cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert        map.put("green", 0x008000);
8319cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert        map.put("grey", 0x808080);
8329cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert        map.put("lime", 0x00FF00);
8339cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert        map.put("maroon", 0x800000);
8349cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert        map.put("navy", 0x000080);
8359cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert        map.put("olive", 0x808000);
8369cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert        map.put("purple", 0x800080);
8379cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert        map.put("red", 0xFF0000);
8389cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert        map.put("silver", 0xC0C0C0);
8399cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert        map.put("teal", 0x008080);
8409cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert        map.put("white", 0xFFFFFF);
8419cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert        map.put("yellow", 0xFFFF00);
8429cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert        return map;
8439cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert    }
8449cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert
8459cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert    /**
8469cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert     * Converts an HTML color (named or numeric) to an integer RGB value.
8479cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert     *
8489cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert     * @param color Non-null color string.
8499cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert     * @return A color value, or {@code -1} if the color string could not be interpreted.
8509cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert     */
8519cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert    private static int getHtmlColor(String color) {
8529cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert        Integer i = COLORS.get(color.toLowerCase());
8539cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert        if (i != null) {
8549cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert            return i;
8559cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert        } else {
8569cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert            try {
8579cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert                return XmlUtils.convertValueToInt(color, -1);
8589cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert            } catch (NumberFormatException nfe) {
8599cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert                return -1;
8609cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert            }
8619cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert        }
8629cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert      }
8639cab7f7684f046934e3d400482dbd1db67c93ee4Bjorn Bringert
8649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
865