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 19b138e2877823a0e9f9d834059a64d4035556562bDaniel Uimport android.app.ActivityThread; 20b138e2877823a0e9f9d834059a64d4035556562bDaniel Uimport android.app.Application; 219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.Resources; 222102de9e1f7444001a5408856c7cf47df050c4e2Daniel Uimport android.graphics.Color; 239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Typeface; 249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.drawable.Drawable; 25105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Projectimport android.text.style.AbsoluteSizeSpan; 26105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Projectimport android.text.style.AlignmentSpan; 275c02d7374f36672e8c3bab8c7cf519ed4a4d8eadDaniel Uimport android.text.style.BackgroundColorSpan; 282102de9e1f7444001a5408856c7cf47df050c4e2Daniel Uimport android.text.style.BulletSpan; 299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.CharacterStyle; 309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.ForegroundColorSpan; 319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.ImageSpan; 329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.ParagraphStyle; 339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.QuoteSpan; 349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.RelativeSizeSpan; 359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.StrikethroughSpan; 369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.StyleSpan; 379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.SubscriptSpan; 389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.SuperscriptSpan; 399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.TypefaceSpan; 409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.URLSpan; 419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.style.UnderlineSpan; 422269d1572e5fcfb725ea55f5764d8c3280d69f6dDianne Hackborn 434037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikasimport org.ccil.cowan.tagsoup.HTMLSchema; 444037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikasimport org.ccil.cowan.tagsoup.Parser; 454037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikasimport org.xml.sax.Attributes; 464037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikasimport org.xml.sax.ContentHandler; 474037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikasimport org.xml.sax.InputSource; 484037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikasimport org.xml.sax.Locator; 494037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikasimport org.xml.sax.SAXException; 504037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikasimport org.xml.sax.XMLReader; 514037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikas 529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.IOException; 539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.StringReader; 541eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel Uimport java.util.HashMap; 551eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel Uimport java.util.Locale; 561eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel Uimport java.util.Map; 57cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel Uimport java.util.regex.Matcher; 58cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel Uimport java.util.regex.Pattern; 599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/** 619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * This class processes HTML strings into displayable styled text. 629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Not all HTML tags are supported. 639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class Html { 659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Retrieves images for HTML <img> tags. 679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static interface ImageGetter { 699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 70d0646dca40ff740bd49755ad60751678b0ccca52Mark Doliner * This method is called when the HTML parser encounters an 719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <img> tag. The <code>source</code> argument is the 729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * string from the "src" attribute; the return value should be 739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * a Drawable representation of the image or <code>null</code> 749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * for a generic replacement image. Make sure you call 759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * setBounds() on your Drawable if it doesn't already have 769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * its bounds set. 779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public Drawable getDrawable(String source); 799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Is notified when HTML tags are encountered that the parser does 839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * not know how to interpret. 849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static interface TagHandler { 869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * This method will be called whenn the HTML parser encounters 889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * a tag that it does not know how to interpret. 899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void handleTag(boolean opening, String tag, 919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Editable output, XMLReader xmlReader); 929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 942102de9e1f7444001a5408856c7cf47df050c4e2Daniel U /** 952102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * Option for {@link #toHtml(Spanned, int)}: Wrap consecutive lines of text delimited by '\n' 962102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * inside <p> elements. {@link BulletSpan}s are ignored. 972102de9e1f7444001a5408856c7cf47df050c4e2Daniel U */ 982102de9e1f7444001a5408856c7cf47df050c4e2Daniel U public static final int TO_HTML_PARAGRAPH_LINES_CONSECUTIVE = 0x00000000; 992102de9e1f7444001a5408856c7cf47df050c4e2Daniel U 1002102de9e1f7444001a5408856c7cf47df050c4e2Daniel U /** 1012102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * Option for {@link #toHtml(Spanned, int)}: Wrap each line of text delimited by '\n' inside a 1022102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * <p> or a <li> element. This allows {@link ParagraphStyle}s attached to be 1032102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * encoded as CSS styles within the corresponding <p> or <li> element. 1042102de9e1f7444001a5408856c7cf47df050c4e2Daniel U */ 1052102de9e1f7444001a5408856c7cf47df050c4e2Daniel U public static final int TO_HTML_PARAGRAPH_LINES_INDIVIDUAL = 0x00000001; 1062102de9e1f7444001a5408856c7cf47df050c4e2Daniel U 1072102de9e1f7444001a5408856c7cf47df050c4e2Daniel U /** 1082102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * Flag indicating that texts inside <p> elements will be separated from other texts with 1092102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * one newline character by default. 1102102de9e1f7444001a5408856c7cf47df050c4e2Daniel U */ 1112102de9e1f7444001a5408856c7cf47df050c4e2Daniel U public static final int FROM_HTML_SEPARATOR_LINE_BREAK_PARAGRAPH = 0x00000001; 1122102de9e1f7444001a5408856c7cf47df050c4e2Daniel U 1132102de9e1f7444001a5408856c7cf47df050c4e2Daniel U /** 1142102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * Flag indicating that texts inside <h1>~<h6> elements will be separated from 1152102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * other texts with one newline character by default. 1162102de9e1f7444001a5408856c7cf47df050c4e2Daniel U */ 1172102de9e1f7444001a5408856c7cf47df050c4e2Daniel U public static final int FROM_HTML_SEPARATOR_LINE_BREAK_HEADING = 0x00000002; 1182102de9e1f7444001a5408856c7cf47df050c4e2Daniel U 1192102de9e1f7444001a5408856c7cf47df050c4e2Daniel U /** 1202102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * Flag indicating that texts inside <li> elements will be separated from other texts 1212102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * with one newline character by default. 1222102de9e1f7444001a5408856c7cf47df050c4e2Daniel U */ 1232102de9e1f7444001a5408856c7cf47df050c4e2Daniel U public static final int FROM_HTML_SEPARATOR_LINE_BREAK_LIST_ITEM = 0x00000004; 1242102de9e1f7444001a5408856c7cf47df050c4e2Daniel U 1252102de9e1f7444001a5408856c7cf47df050c4e2Daniel U /** 1262102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * Flag indicating that texts inside <ul> elements will be separated from other texts 1272102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * with one newline character by default. 1282102de9e1f7444001a5408856c7cf47df050c4e2Daniel U */ 1292102de9e1f7444001a5408856c7cf47df050c4e2Daniel U public static final int FROM_HTML_SEPARATOR_LINE_BREAK_LIST = 0x00000008; 1302102de9e1f7444001a5408856c7cf47df050c4e2Daniel U 1312102de9e1f7444001a5408856c7cf47df050c4e2Daniel U /** 1322102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * Flag indicating that texts inside <div> elements will be separated from other texts 1332102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * with one newline character by default. 1342102de9e1f7444001a5408856c7cf47df050c4e2Daniel U */ 1352102de9e1f7444001a5408856c7cf47df050c4e2Daniel U public static final int FROM_HTML_SEPARATOR_LINE_BREAK_DIV = 0x00000010; 1362102de9e1f7444001a5408856c7cf47df050c4e2Daniel U 1372102de9e1f7444001a5408856c7cf47df050c4e2Daniel U /** 1382102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * Flag indicating that texts inside <blockquote> elements will be separated from other 1392102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * texts with one newline character by default. 1402102de9e1f7444001a5408856c7cf47df050c4e2Daniel U */ 1412102de9e1f7444001a5408856c7cf47df050c4e2Daniel U public static final int FROM_HTML_SEPARATOR_LINE_BREAK_BLOCKQUOTE = 0x00000020; 1422102de9e1f7444001a5408856c7cf47df050c4e2Daniel U 1432102de9e1f7444001a5408856c7cf47df050c4e2Daniel U /** 1442102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * Flag indicating that CSS color values should be used instead of those defined in 1452102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * {@link Color}. 1462102de9e1f7444001a5408856c7cf47df050c4e2Daniel U */ 1472102de9e1f7444001a5408856c7cf47df050c4e2Daniel U public static final int FROM_HTML_OPTION_USE_CSS_COLORS = 0x00000100; 1482102de9e1f7444001a5408856c7cf47df050c4e2Daniel U 1492102de9e1f7444001a5408856c7cf47df050c4e2Daniel U /** 1502102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * Flags for {@link #fromHtml(String, int, ImageGetter, TagHandler)}: Separate block-level 1512102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * elements with blank lines (two newline characters) in between. This is the legacy behavior 1522102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * prior to N. 1532102de9e1f7444001a5408856c7cf47df050c4e2Daniel U */ 1542102de9e1f7444001a5408856c7cf47df050c4e2Daniel U public static final int FROM_HTML_MODE_LEGACY = 0x00000000; 1552102de9e1f7444001a5408856c7cf47df050c4e2Daniel U 1562102de9e1f7444001a5408856c7cf47df050c4e2Daniel U /** 1572102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * Flags for {@link #fromHtml(String, int, ImageGetter, TagHandler)}: Separate block-level 1582102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * elements with line breaks (single newline character) in between. This inverts the 1592102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * {@link Spanned} to HTML string conversion done with the option 1602102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * {@link #TO_HTML_PARAGRAPH_LINES_INDIVIDUAL}. 1612102de9e1f7444001a5408856c7cf47df050c4e2Daniel U */ 1622102de9e1f7444001a5408856c7cf47df050c4e2Daniel U public static final int FROM_HTML_MODE_COMPACT = 1632102de9e1f7444001a5408856c7cf47df050c4e2Daniel U FROM_HTML_SEPARATOR_LINE_BREAK_PARAGRAPH 1642102de9e1f7444001a5408856c7cf47df050c4e2Daniel U | FROM_HTML_SEPARATOR_LINE_BREAK_HEADING 1652102de9e1f7444001a5408856c7cf47df050c4e2Daniel U | FROM_HTML_SEPARATOR_LINE_BREAK_LIST_ITEM 1662102de9e1f7444001a5408856c7cf47df050c4e2Daniel U | FROM_HTML_SEPARATOR_LINE_BREAK_LIST 1672102de9e1f7444001a5408856c7cf47df050c4e2Daniel U | FROM_HTML_SEPARATOR_LINE_BREAK_DIV 1682102de9e1f7444001a5408856c7cf47df050c4e2Daniel U | FROM_HTML_SEPARATOR_LINE_BREAK_BLOCKQUOTE; 1692102de9e1f7444001a5408856c7cf47df050c4e2Daniel U 170ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U /** 171ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U * The bit which indicates if lines delimited by '\n' will be grouped into <p> elements. 172ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U */ 173ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U private static final int TO_HTML_PARAGRAPH_FLAG = 0x00000001; 174ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U 1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private Html() { } 1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1782102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * Returns displayable styled text from the provided HTML string with the legacy flags 1792102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * {@link #FROM_HTML_MODE_LEGACY}. 1802102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * 1812102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * @deprecated use {@link #fromHtml(String, int)} instead. 1822102de9e1f7444001a5408856c7cf47df050c4e2Daniel U */ 1832102de9e1f7444001a5408856c7cf47df050c4e2Daniel U @Deprecated 1842102de9e1f7444001a5408856c7cf47df050c4e2Daniel U public static Spanned fromHtml(String source) { 1852102de9e1f7444001a5408856c7cf47df050c4e2Daniel U return fromHtml(source, FROM_HTML_MODE_LEGACY, null, null); 1862102de9e1f7444001a5408856c7cf47df050c4e2Daniel U } 1872102de9e1f7444001a5408856c7cf47df050c4e2Daniel U 1882102de9e1f7444001a5408856c7cf47df050c4e2Daniel U /** 1892102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * Returns displayable styled text from the provided HTML string. Any <img> tags in the 1902102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * HTML will display as a generic replacement image which your program can then go through and 1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * replace with real images. 1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <p>This uses TagSoup to handle real HTML, including all of the brokenness found in the wild. 1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1952102de9e1f7444001a5408856c7cf47df050c4e2Daniel U public static Spanned fromHtml(String source, int flags) { 1962102de9e1f7444001a5408856c7cf47df050c4e2Daniel U return fromHtml(source, flags, null, null); 1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Lazy initialization holder for HTML parser. This class will 2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * a) be preloaded by the zygote, or b) not loaded until absolutely 2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * necessary. 2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static class HtmlParser { 2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final HTMLSchema schema = new HTMLSchema(); 2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 2092102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * Returns displayable styled text from the provided HTML string with the legacy flags 2102102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * {@link #FROM_HTML_MODE_LEGACY}. 2112102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * 2122102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * @deprecated use {@link #fromHtml(String, int, ImageGetter, TagHandler)} instead. 2132102de9e1f7444001a5408856c7cf47df050c4e2Daniel U */ 2142102de9e1f7444001a5408856c7cf47df050c4e2Daniel U @Deprecated 2152102de9e1f7444001a5408856c7cf47df050c4e2Daniel U public static Spanned fromHtml(String source, ImageGetter imageGetter, TagHandler tagHandler) { 2162102de9e1f7444001a5408856c7cf47df050c4e2Daniel U return fromHtml(source, FROM_HTML_MODE_LEGACY, imageGetter, tagHandler); 2172102de9e1f7444001a5408856c7cf47df050c4e2Daniel U } 2182102de9e1f7444001a5408856c7cf47df050c4e2Daniel U 2192102de9e1f7444001a5408856c7cf47df050c4e2Daniel U /** 2202102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * Returns displayable styled text from the provided HTML string. Any <img> tags in the 2212102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * HTML will use the specified ImageGetter to request a representation of the image (use null 2222102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * if you don't want this) and the specified TagHandler to handle unknown tags (specify null if 2232102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * you don't want this). 2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * <p>This uses TagSoup to handle real HTML, including all of the brokenness found in the wild. 2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 2272102de9e1f7444001a5408856c7cf47df050c4e2Daniel U public static Spanned fromHtml(String source, int flags, ImageGetter imageGetter, 2282102de9e1f7444001a5408856c7cf47df050c4e2Daniel U TagHandler tagHandler) { 2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Parser parser = new Parser(); 2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project parser.setProperty(Parser.schemaProperty, HtmlParser.schema); 2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (org.xml.sax.SAXNotRecognizedException e) { 2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Should not happen. 2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project throw new RuntimeException(e); 2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (org.xml.sax.SAXNotSupportedException e) { 2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Should not happen. 2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project throw new RuntimeException(e); 2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project HtmlToSpannedConverter converter = 2412102de9e1f7444001a5408856c7cf47df050c4e2Daniel U new HtmlToSpannedConverter(source, imageGetter, tagHandler, parser, flags); 2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return converter.convert(); 2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 2462102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * @deprecated use {@link #toHtml(Spanned, int)} instead. 2472102de9e1f7444001a5408856c7cf47df050c4e2Daniel U */ 2482102de9e1f7444001a5408856c7cf47df050c4e2Daniel U @Deprecated 2492102de9e1f7444001a5408856c7cf47df050c4e2Daniel U public static String toHtml(Spanned text) { 2502102de9e1f7444001a5408856c7cf47df050c4e2Daniel U return toHtml(text, TO_HTML_PARAGRAPH_LINES_CONSECUTIVE); 2512102de9e1f7444001a5408856c7cf47df050c4e2Daniel U } 2522102de9e1f7444001a5408856c7cf47df050c4e2Daniel U 2532102de9e1f7444001a5408856c7cf47df050c4e2Daniel U /** 254c55277d689dee83f58f63f56a0e26debff62aa56Raph Levien * Returns an HTML representation of the provided Spanned text. A best effort is 255c55277d689dee83f58f63f56a0e26debff62aa56Raph Levien * made to add HTML tags corresponding to spans. Also note that HTML metacharacters 256c55277d689dee83f58f63f56a0e26debff62aa56Raph Levien * (such as "<" and "&") within the input text are escaped. 257c55277d689dee83f58f63f56a0e26debff62aa56Raph Levien * 258c55277d689dee83f58f63f56a0e26debff62aa56Raph Levien * @param text input text to convert 2592102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * @param option one of {@link #TO_HTML_PARAGRAPH_LINES_CONSECUTIVE} or 2602102de9e1f7444001a5408856c7cf47df050c4e2Daniel U * {@link #TO_HTML_PARAGRAPH_LINES_INDIVIDUAL} 261c55277d689dee83f58f63f56a0e26debff62aa56Raph Levien * @return string containing input converted to HTML 2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 2632102de9e1f7444001a5408856c7cf47df050c4e2Daniel U public static String toHtml(Spanned text, int option) { 2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project StringBuilder out = new StringBuilder(); 265ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U withinHtml(out, text, option); 266105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project return out.toString(); 267105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project } 268105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project 269acb69bb909d098cea284df47d794c17171d84c91Dianne Hackborn /** 270acb69bb909d098cea284df47d794c17171d84c91Dianne Hackborn * Returns an HTML escaped representation of the given plain text. 271acb69bb909d098cea284df47d794c17171d84c91Dianne Hackborn */ 272acb69bb909d098cea284df47d794c17171d84c91Dianne Hackborn public static String escapeHtml(CharSequence text) { 273acb69bb909d098cea284df47d794c17171d84c91Dianne Hackborn StringBuilder out = new StringBuilder(); 274acb69bb909d098cea284df47d794c17171d84c91Dianne Hackborn withinStyle(out, text, 0, text.length()); 275acb69bb909d098cea284df47d794c17171d84c91Dianne Hackborn return out.toString(); 276acb69bb909d098cea284df47d794c17171d84c91Dianne Hackborn } 277acb69bb909d098cea284df47d794c17171d84c91Dianne Hackborn 278ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U private static void withinHtml(StringBuilder out, Spanned text, int option) { 279ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U if ((option & TO_HTML_PARAGRAPH_FLAG) == TO_HTML_PARAGRAPH_LINES_CONSECUTIVE) { 280ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U encodeTextAlignmentByDiv(out, text, option); 281ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U return; 282ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U } 283ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U 284ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U withinDiv(out, text, 0, text.length(), option); 285ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U } 286ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U 287ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U private static void encodeTextAlignmentByDiv(StringBuilder out, Spanned text, int option) { 2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int len = text.length(); 2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int next; 2915c02d7374f36672e8c3bab8c7cf519ed4a4d8eadDaniel U for (int i = 0; i < len; i = next) { 292105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project next = text.nextSpanTransition(i, len, ParagraphStyle.class); 293105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project ParagraphStyle[] style = text.getSpans(i, next, ParagraphStyle.class); 29432048300e917c9181927ac017d02855bbde940efSatoshi Kataoka String elements = " "; 29500ba76670fd06d9c51cce36a74c384a212f705b5Eric Fischer boolean needDiv = false; 29600ba76670fd06d9c51cce36a74c384a212f705b5Eric Fischer 297105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project for(int j = 0; j < style.length; j++) { 298105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project if (style[j] instanceof AlignmentSpan) { 299a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy Layout.Alignment align = 300105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project ((AlignmentSpan) style[j]).getAlignment(); 30100ba76670fd06d9c51cce36a74c384a212f705b5Eric Fischer needDiv = true; 302105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project if (align == Layout.Alignment.ALIGN_CENTER) { 30332048300e917c9181927ac017d02855bbde940efSatoshi Kataoka elements = "align=\"center\" " + elements; 304105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project } else if (align == Layout.Alignment.ALIGN_OPPOSITE) { 30532048300e917c9181927ac017d02855bbde940efSatoshi Kataoka elements = "align=\"right\" " + elements; 306105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project } else { 30732048300e917c9181927ac017d02855bbde940efSatoshi Kataoka elements = "align=\"left\" " + elements; 308105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project } 309105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project } 310105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project } 31100ba76670fd06d9c51cce36a74c384a212f705b5Eric Fischer if (needDiv) { 312a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy out.append("<div ").append(elements).append(">"); 313105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project } 314105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project 315ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U withinDiv(out, text, i, next, option); 316105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project 31700ba76670fd06d9c51cce36a74c384a212f705b5Eric Fischer if (needDiv) { 318105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project out.append("</div>"); 319105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project } 320105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project } 321105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project } 322105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project 323ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U private static void withinDiv(StringBuilder out, Spanned text, int start, int end, 324ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U int option) { 325105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project int next; 326105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project for (int i = start; i < end; i = next) { 327105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project next = text.nextSpanTransition(i, end, QuoteSpan.class); 3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project QuoteSpan[] quotes = text.getSpans(i, next, QuoteSpan.class); 3299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 330a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy for (QuoteSpan quote : quotes) { 3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project out.append("<blockquote>"); 3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 334ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U withinBlockquote(out, text, i, next, option); 3359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 336a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy for (QuoteSpan quote : quotes) { 3379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project out.append("</blockquote>\n"); 3389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 342ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U private static String getTextDirection(Spanned text, int start, int end) { 343aa0af8e86278d8b0935c95a5ff06c80532bf7733Roozbeh Pournader if (TextDirectionHeuristics.FIRSTSTRONG_LTR.isRtl(text, start, end - start)) { 344aa0af8e86278d8b0935c95a5ff06c80532bf7733Roozbeh Pournader return " dir=\"rtl\""; 345aa0af8e86278d8b0935c95a5ff06c80532bf7733Roozbeh Pournader } else { 346aa0af8e86278d8b0935c95a5ff06c80532bf7733Roozbeh Pournader return " dir=\"ltr\""; 347ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U } 348ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U } 349ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U 3504a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U private static String getTextStyles(Spanned text, int start, int end, 3514a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U boolean forceNoVerticalMargin, boolean includeTextAlign) { 3524a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U String margin = null; 3534a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U String textAlign = null; 3544a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U 3554a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U if (forceNoVerticalMargin) { 3564a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U margin = "margin-top:0; margin-bottom:0;"; 3574a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U } 3584a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U if (includeTextAlign) { 3594a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U final AlignmentSpan[] alignmentSpans = text.getSpans(start, end, AlignmentSpan.class); 3604a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U 3614a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U // Only use the last AlignmentSpan with flag SPAN_PARAGRAPH 3624a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U for (int i = alignmentSpans.length - 1; i >= 0; i--) { 3634a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U AlignmentSpan s = alignmentSpans[i]; 3644a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U if ((text.getSpanFlags(s) & Spanned.SPAN_PARAGRAPH) == Spanned.SPAN_PARAGRAPH) { 3654a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U final Layout.Alignment alignment = s.getAlignment(); 3664a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U if (alignment == Layout.Alignment.ALIGN_NORMAL) { 3674a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U textAlign = "text-align:start;"; 3684a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U } else if (alignment == Layout.Alignment.ALIGN_CENTER) { 3694a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U textAlign = "text-align:center;"; 3704a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U } else if (alignment == Layout.Alignment.ALIGN_OPPOSITE) { 3714a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U textAlign = "text-align:end;"; 3724a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U } 3734a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U break; 3744a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U } 375ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U } 376cd4161b134387457bac9943db569afbfa427b87bFabrice Di Meglio } 377ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U 3784a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U if (margin == null && textAlign == null) { 3794a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U return ""; 3804a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U } 3814a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U 3824a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U final StringBuilder style = new StringBuilder(" style=\""); 3834a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U if (margin != null && textAlign != null) { 3844a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U style.append(margin).append(" ").append(textAlign); 3854a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U } else if (margin != null) { 3864a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U style.append(margin); 3874a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U } else if (textAlign != null) { 3884a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U style.append(textAlign); 3894a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U } 3904a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U 3914a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U return style.append("\"").toString(); 392cd4161b134387457bac9943db569afbfa427b87bFabrice Di Meglio } 393cd4161b134387457bac9943db569afbfa427b87bFabrice Di Meglio 394ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U private static void withinBlockquote(StringBuilder out, Spanned text, int start, int end, 395ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U int option) { 396ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U if ((option & TO_HTML_PARAGRAPH_FLAG) == TO_HTML_PARAGRAPH_LINES_CONSECUTIVE) { 397ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U withinBlockquoteConsecutive(out, text, start, end); 398ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U } else { 399ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U withinBlockquoteIndividual(out, text, start, end); 400ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U } 401ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U } 402ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U 403ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U private static void withinBlockquoteIndividual(StringBuilder out, Spanned text, int start, 404ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U int end) { 405ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U boolean isInList = false; 406ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U int next; 407ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U for (int i = start; i <= end; i = next) { 408ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U next = TextUtils.indexOf(text, '\n', i, end); 409ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U if (next < 0) { 410ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U next = end; 411ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U } 412ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U 4134a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U if (next == i) { 4144a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U if (isInList) { 4154a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U // Current paragraph is no longer a list item; close the previously opened list 4164a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U isInList = false; 4174a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U out.append("</ul>\n"); 4184a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U } 4194a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U out.append("<br>\n"); 4204a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U } else { 4214a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U boolean isListItem = false; 4224a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U ParagraphStyle[] paragraphStyles = text.getSpans(i, next, ParagraphStyle.class); 4234a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U for (ParagraphStyle paragraphStyle : paragraphStyles) { 4244a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U final int spanFlags = text.getSpanFlags(paragraphStyle); 4254a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U if ((spanFlags & Spanned.SPAN_PARAGRAPH) == Spanned.SPAN_PARAGRAPH 4264a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U && paragraphStyle instanceof BulletSpan) { 4274a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U isListItem = true; 4284a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U break; 4294a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U } 430ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U } 431ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U 4324a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U if (isListItem && !isInList) { 4334a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U // Current paragraph is the first item in a list 4344a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U isInList = true; 4354a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U out.append("<ul") 4364a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U .append(getTextStyles(text, i, next, true, false)) 4374a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U .append(">\n"); 4384a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U } 439ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U 4404a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U if (isInList && !isListItem) { 4414a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U // Current paragraph is no longer a list item; close the previously opened list 4424a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U isInList = false; 4434a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U out.append("</ul>\n"); 4444a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U } 445ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U 4464a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U String tagType = isListItem ? "li" : "p"; 4474a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U out.append("<").append(tagType) 4484a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U .append(getTextDirection(text, i, next)) 4494a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U .append(getTextStyles(text, i, next, !isListItem, true)) 4504a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U .append(">"); 451ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U 452ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U withinParagraph(out, text, i, next); 453ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U 4544a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U out.append("</"); 4554a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U out.append(tagType); 4564a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U out.append(">\n"); 457ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U 4584a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U if (next == end && isInList) { 4594a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U isInList = false; 4604a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U out.append("</ul>\n"); 4614a70f09e39ac7c5f57b0f6dd44475641c68afafdDaniel U } 462ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U } 463ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U 464ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U next++; 465ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U } 466ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U } 467ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U 468ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U private static void withinBlockquoteConsecutive(StringBuilder out, Spanned text, int start, 469ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U int end) { 470ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U out.append("<p").append(getTextDirection(text, start, end)).append(">"); 4719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 4729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int next; 4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = start; i < end; i = next) { 4749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project next = TextUtils.indexOf(text, '\n', i, end); 4759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (next < 0) { 4769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project next = end; 4779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 4799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int nl = 0; 4809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 4819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project while (next < end && text.charAt(next) == '\n') { 4829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project nl++; 4839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project next++; 4849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 486ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U withinParagraph(out, text, i, next - nl); 487ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U 488ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U if (nl == 1) { 489ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U out.append("<br>\n"); 490ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U } else { 491ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U for (int j = 2; j < nl; j++) { 492ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U out.append("<br>"); 493ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U } 494ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U if (next != end) { 495ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U /* Paragraph should be closed and reopened */ 496ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U out.append("</p>\n"); 497ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U out.append("<p").append(getTextDirection(text, start, end)).append(">"); 498ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U } 4992243ae17a9df91d589c8580f3ae499b63de91166Roozbeh Pournader } 5009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project out.append("</p>\n"); 5039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 505ca124ab1e25b4709c46de50ae8536d4a9338c8cbDaniel U private static void withinParagraph(StringBuilder out, Spanned text, int start, int end) { 5069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int next; 5079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = start; i < end; i = next) { 5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project next = text.nextSpanTransition(i, end, CharacterStyle.class); 5095c02d7374f36672e8c3bab8c7cf519ed4a4d8eadDaniel U CharacterStyle[] style = text.getSpans(i, next, CharacterStyle.class); 5109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int j = 0; j < style.length; j++) { 5129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (style[j] instanceof StyleSpan) { 5139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int s = ((StyleSpan) style[j]).getStyle(); 5149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if ((s & Typeface.BOLD) != 0) { 5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project out.append("<b>"); 5179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if ((s & Typeface.ITALIC) != 0) { 5199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project out.append("<i>"); 5209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (style[j] instanceof TypefaceSpan) { 5239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String s = ((TypefaceSpan) style[j]).getFamily(); 5249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5258e71a397c6542d6a37cd59ea8b53236ac2dba86aRaph Levien if ("monospace".equals(s)) { 5269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project out.append("<tt>"); 5279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (style[j] instanceof SuperscriptSpan) { 5309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project out.append("<sup>"); 5319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (style[j] instanceof SubscriptSpan) { 5339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project out.append("<sub>"); 5349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (style[j] instanceof UnderlineSpan) { 5369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project out.append("<u>"); 5379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (style[j] instanceof StrikethroughSpan) { 5395c02d7374f36672e8c3bab8c7cf519ed4a4d8eadDaniel U out.append("<span style=\"text-decoration:line-through;\">"); 5409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (style[j] instanceof URLSpan) { 5429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project out.append("<a href=\""); 5439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project out.append(((URLSpan) style[j]).getURL()); 5449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project out.append("\">"); 5459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (style[j] instanceof ImageSpan) { 5479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project out.append("<img src=\""); 5489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project out.append(((ImageSpan) style[j]).getSource()); 5499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project out.append("\">"); 5509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Don't output the dummy character underlying the image. 5529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project i = next; 5539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 554105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project if (style[j] instanceof AbsoluteSizeSpan) { 555b138e2877823a0e9f9d834059a64d4035556562bDaniel U AbsoluteSizeSpan s = ((AbsoluteSizeSpan) style[j]); 556b138e2877823a0e9f9d834059a64d4035556562bDaniel U float sizeDip = s.getSize(); 557b138e2877823a0e9f9d834059a64d4035556562bDaniel U if (!s.getDip()) { 558b138e2877823a0e9f9d834059a64d4035556562bDaniel U Application application = ActivityThread.currentApplication(); 559b138e2877823a0e9f9d834059a64d4035556562bDaniel U sizeDip /= application.getResources().getDisplayMetrics().density; 560b138e2877823a0e9f9d834059a64d4035556562bDaniel U } 561b138e2877823a0e9f9d834059a64d4035556562bDaniel U 562b138e2877823a0e9f9d834059a64d4035556562bDaniel U // px in CSS is the equivalance of dip in Android 563b138e2877823a0e9f9d834059a64d4035556562bDaniel U out.append(String.format("<span style=\"font-size:%.0fpx\";>", sizeDip)); 564b138e2877823a0e9f9d834059a64d4035556562bDaniel U } 565b138e2877823a0e9f9d834059a64d4035556562bDaniel U if (style[j] instanceof RelativeSizeSpan) { 566b138e2877823a0e9f9d834059a64d4035556562bDaniel U float sizeEm = ((RelativeSizeSpan) style[j]).getSizeChange(); 567b138e2877823a0e9f9d834059a64d4035556562bDaniel U out.append(String.format("<span style=\"font-size:%.2fem;\">", sizeEm)); 568105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project } 569105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project if (style[j] instanceof ForegroundColorSpan) { 5705c02d7374f36672e8c3bab8c7cf519ed4a4d8eadDaniel U int color = ((ForegroundColorSpan) style[j]).getForegroundColor(); 5715c02d7374f36672e8c3bab8c7cf519ed4a4d8eadDaniel U out.append(String.format("<span style=\"color:#%06X;\">", 0xFFFFFF & color)); 5725c02d7374f36672e8c3bab8c7cf519ed4a4d8eadDaniel U } 5735c02d7374f36672e8c3bab8c7cf519ed4a4d8eadDaniel U if (style[j] instanceof BackgroundColorSpan) { 5745c02d7374f36672e8c3bab8c7cf519ed4a4d8eadDaniel U int color = ((BackgroundColorSpan) style[j]).getBackgroundColor(); 5755c02d7374f36672e8c3bab8c7cf519ed4a4d8eadDaniel U out.append(String.format("<span style=\"background-color:#%06X;\">", 5765c02d7374f36672e8c3bab8c7cf519ed4a4d8eadDaniel U 0xFFFFFF & color)); 577105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project } 5789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project withinStyle(out, text, i, next); 5819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int j = style.length - 1; j >= 0; j--) { 5835c02d7374f36672e8c3bab8c7cf519ed4a4d8eadDaniel U if (style[j] instanceof BackgroundColorSpan) { 5845c02d7374f36672e8c3bab8c7cf519ed4a4d8eadDaniel U out.append("</span>"); 5855c02d7374f36672e8c3bab8c7cf519ed4a4d8eadDaniel U } 586105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project if (style[j] instanceof ForegroundColorSpan) { 5875c02d7374f36672e8c3bab8c7cf519ed4a4d8eadDaniel U out.append("</span>"); 588105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project } 589b138e2877823a0e9f9d834059a64d4035556562bDaniel U if (style[j] instanceof RelativeSizeSpan) { 590b138e2877823a0e9f9d834059a64d4035556562bDaniel U out.append("</span>"); 591b138e2877823a0e9f9d834059a64d4035556562bDaniel U } 592105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project if (style[j] instanceof AbsoluteSizeSpan) { 593b138e2877823a0e9f9d834059a64d4035556562bDaniel U out.append("</span>"); 594105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project } 5959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (style[j] instanceof URLSpan) { 5969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project out.append("</a>"); 5979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (style[j] instanceof StrikethroughSpan) { 5995c02d7374f36672e8c3bab8c7cf519ed4a4d8eadDaniel U out.append("</span>"); 6009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (style[j] instanceof UnderlineSpan) { 6029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project out.append("</u>"); 6039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (style[j] instanceof SubscriptSpan) { 6059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project out.append("</sub>"); 6069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (style[j] instanceof SuperscriptSpan) { 6089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project out.append("</sup>"); 6099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (style[j] instanceof TypefaceSpan) { 6119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String s = ((TypefaceSpan) style[j]).getFamily(); 6129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (s.equals("monospace")) { 6149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project out.append("</tt>"); 6159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (style[j] instanceof StyleSpan) { 6189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int s = ((StyleSpan) style[j]).getStyle(); 6199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if ((s & Typeface.BOLD) != 0) { 6219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project out.append("</b>"); 6229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if ((s & Typeface.ITALIC) != 0) { 6249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project out.append("</i>"); 6259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 631acb69bb909d098cea284df47d794c17171d84c91Dianne Hackborn private static void withinStyle(StringBuilder out, CharSequence text, 6329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int start, int end) { 6339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = start; i < end; i++) { 6349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char c = text.charAt(i); 6359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (c == '<') { 6379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project out.append("<"); 6389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (c == '>') { 6399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project out.append(">"); 6409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (c == '&') { 6419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project out.append("&"); 6423d4764159c78857bbd3305f4fa9c11c70e742d88Victoria Lease } else if (c >= 0xD800 && c <= 0xDFFF) { 6433d4764159c78857bbd3305f4fa9c11c70e742d88Victoria Lease if (c < 0xDC00 && i + 1 < end) { 6443d4764159c78857bbd3305f4fa9c11c70e742d88Victoria Lease char d = text.charAt(i + 1); 6453d4764159c78857bbd3305f4fa9c11c70e742d88Victoria Lease if (d >= 0xDC00 && d <= 0xDFFF) { 6463d4764159c78857bbd3305f4fa9c11c70e742d88Victoria Lease i++; 6473d4764159c78857bbd3305f4fa9c11c70e742d88Victoria Lease int codepoint = 0x010000 | (int) c - 0xD800 << 10 | (int) d - 0xDC00; 6483d4764159c78857bbd3305f4fa9c11c70e742d88Victoria Lease out.append("&#").append(codepoint).append(";"); 6493d4764159c78857bbd3305f4fa9c11c70e742d88Victoria Lease } 6503d4764159c78857bbd3305f4fa9c11c70e742d88Victoria Lease } 6519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (c > 0x7E || c < ' ') { 652a8f6d5f0720f400b6f59b0809aaefea83c5f51d4Romain Guy out.append("&#").append((int) c).append(";"); 6539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (c == ' ') { 6549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project while (i + 1 < end && text.charAt(i + 1) == ' ') { 6559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project out.append(" "); 6569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project i++; 6579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project out.append(' '); 6609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 6619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project out.append(c); 6629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 6669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectclass HtmlToSpannedConverter implements ContentHandler { 6689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 669cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U private static final float[] HEADING_SIZES = { 6709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1.5f, 1.4f, 1.3f, 1.2f, 1.1f, 1f, 6719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project }; 6729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private String mSource; 6749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private XMLReader mReader; 6759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private SpannableStringBuilder mSpannableStringBuilder; 6769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private Html.ImageGetter mImageGetter; 6779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private Html.TagHandler mTagHandler; 6782102de9e1f7444001a5408856c7cf47df050c4e2Daniel U private int mFlags; 6799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 680cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U private static Pattern sTextAlignPattern; 6818b36c0bbd1503c61c111feac939193c47f812190Daniel U private static Pattern sForegroundColorPattern; 6828b36c0bbd1503c61c111feac939193c47f812190Daniel U private static Pattern sBackgroundColorPattern; 6838b36c0bbd1503c61c111feac939193c47f812190Daniel U private static Pattern sTextDecorationPattern; 684cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U 6851eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U /** 6861eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U * Name-value mapping of HTML/CSS colors which have different values in {@link Color}. 6871eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U */ 6881eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U private static final Map<String, Integer> sColorMap; 6891eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U 6901eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U static { 6911eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U sColorMap = new HashMap<>(); 6921eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U sColorMap.put("darkgray", 0xFFA9A9A9); 6931eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U sColorMap.put("gray", 0xFF808080); 6941eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U sColorMap.put("lightgray", 0xFFD3D3D3); 6951eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U sColorMap.put("darkgrey", 0xFFA9A9A9); 6961eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U sColorMap.put("grey", 0xFF808080); 6971eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U sColorMap.put("lightgrey", 0xFFD3D3D3); 6981eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U sColorMap.put("green", 0xFF008000); 6991eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U } 7001eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U 701cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U private static Pattern getTextAlignPattern() { 702cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U if (sTextAlignPattern == null) { 703cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U sTextAlignPattern = Pattern.compile("(?:\\s+|\\A)text-align\\s*:\\s*(\\S*)\\b"); 704cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U } 705cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U return sTextAlignPattern; 706cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U } 707cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U 7088b36c0bbd1503c61c111feac939193c47f812190Daniel U private static Pattern getForegroundColorPattern() { 7098b36c0bbd1503c61c111feac939193c47f812190Daniel U if (sForegroundColorPattern == null) { 7108b36c0bbd1503c61c111feac939193c47f812190Daniel U sForegroundColorPattern = Pattern.compile( 7118b36c0bbd1503c61c111feac939193c47f812190Daniel U "(?:\\s+|\\A)color\\s*:\\s*(\\S*)\\b"); 7128b36c0bbd1503c61c111feac939193c47f812190Daniel U } 7138b36c0bbd1503c61c111feac939193c47f812190Daniel U return sForegroundColorPattern; 7148b36c0bbd1503c61c111feac939193c47f812190Daniel U } 7158b36c0bbd1503c61c111feac939193c47f812190Daniel U 7168b36c0bbd1503c61c111feac939193c47f812190Daniel U private static Pattern getBackgroundColorPattern() { 7178b36c0bbd1503c61c111feac939193c47f812190Daniel U if (sBackgroundColorPattern == null) { 7188b36c0bbd1503c61c111feac939193c47f812190Daniel U sBackgroundColorPattern = Pattern.compile( 7198b36c0bbd1503c61c111feac939193c47f812190Daniel U "(?:\\s+|\\A)background(?:-color)?\\s*:\\s*(\\S*)\\b"); 7208b36c0bbd1503c61c111feac939193c47f812190Daniel U } 7218b36c0bbd1503c61c111feac939193c47f812190Daniel U return sBackgroundColorPattern; 7228b36c0bbd1503c61c111feac939193c47f812190Daniel U } 7238b36c0bbd1503c61c111feac939193c47f812190Daniel U 7248b36c0bbd1503c61c111feac939193c47f812190Daniel U private static Pattern getTextDecorationPattern() { 7258b36c0bbd1503c61c111feac939193c47f812190Daniel U if (sTextDecorationPattern == null) { 7268b36c0bbd1503c61c111feac939193c47f812190Daniel U sTextDecorationPattern = Pattern.compile( 7278b36c0bbd1503c61c111feac939193c47f812190Daniel U "(?:\\s+|\\A)text-decoration\\s*:\\s*(\\S*)\\b"); 7288b36c0bbd1503c61c111feac939193c47f812190Daniel U } 7298b36c0bbd1503c61c111feac939193c47f812190Daniel U return sTextDecorationPattern; 7308b36c0bbd1503c61c111feac939193c47f812190Daniel U } 7318b36c0bbd1503c61c111feac939193c47f812190Daniel U 7322102de9e1f7444001a5408856c7cf47df050c4e2Daniel U public HtmlToSpannedConverter( String source, Html.ImageGetter imageGetter, 7332102de9e1f7444001a5408856c7cf47df050c4e2Daniel U Html.TagHandler tagHandler, Parser parser, int flags) { 7349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSource = source; 7359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSpannableStringBuilder = new SpannableStringBuilder(); 7369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mImageGetter = imageGetter; 7379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mTagHandler = tagHandler; 7389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mReader = parser; 7392102de9e1f7444001a5408856c7cf47df050c4e2Daniel U mFlags = flags; 7409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 7429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public Spanned convert() { 7439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 7449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mReader.setContentHandler(this); 7459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 7469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mReader.parse(new InputSource(new StringReader(mSource))); 7479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (IOException e) { 7489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // We are reading from a string. There should not be IO problems. 7499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project throw new RuntimeException(e); 7509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (SAXException e) { 7519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // TagSoup doesn't throw parse exceptions. 7529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project throw new RuntimeException(e); 7539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 7559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Fix flags and range for paragraph-type markup. 7569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Object[] obj = mSpannableStringBuilder.getSpans(0, mSpannableStringBuilder.length(), ParagraphStyle.class); 7579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = 0; i < obj.length; i++) { 7589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int start = mSpannableStringBuilder.getSpanStart(obj[i]); 7599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int end = mSpannableStringBuilder.getSpanEnd(obj[i]); 7609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 7619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // If the last line of the range is blank, back off by one. 7629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (end - 2 >= 0) { 7639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mSpannableStringBuilder.charAt(end - 1) == '\n' && 7649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSpannableStringBuilder.charAt(end - 2) == '\n') { 7659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project end--; 7669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 7699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (end == start) { 7709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSpannableStringBuilder.removeSpan(obj[i]); 7719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 7729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSpannableStringBuilder.setSpan(obj[i], start, end, Spannable.SPAN_PARAGRAPH); 7739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 7769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mSpannableStringBuilder; 7779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 7799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void handleStartTag(String tag, Attributes attributes) { 7809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (tag.equalsIgnoreCase("br")) { 7819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // We don't need to handle this. TagSoup will ensure that there's a </br> for each <br> 782cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U // so we can safely emit the linebreaks when we handle the close tag. 7839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.equalsIgnoreCase("p")) { 784cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U startBlockElement(mSpannableStringBuilder, attributes, getMarginParagraph()); 7858b36c0bbd1503c61c111feac939193c47f812190Daniel U startCssStyle(mSpannableStringBuilder, attributes); 7868b36c0bbd1503c61c111feac939193c47f812190Daniel U } else if (tag.equalsIgnoreCase("ul")) { 7878b36c0bbd1503c61c111feac939193c47f812190Daniel U startBlockElement(mSpannableStringBuilder, attributes, getMarginList()); 7888b36c0bbd1503c61c111feac939193c47f812190Daniel U } else if (tag.equalsIgnoreCase("li")) { 7898b36c0bbd1503c61c111feac939193c47f812190Daniel U startLi(mSpannableStringBuilder, attributes); 7909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.equalsIgnoreCase("div")) { 791cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U startBlockElement(mSpannableStringBuilder, attributes, getMarginDiv()); 7928b36c0bbd1503c61c111feac939193c47f812190Daniel U } else if (tag.equalsIgnoreCase("span")) { 7938b36c0bbd1503c61c111feac939193c47f812190Daniel U startCssStyle(mSpannableStringBuilder, attributes); 79494d5e9ad6533864f098b99c231d69c77230a828fRomain Guy } else if (tag.equalsIgnoreCase("strong")) { 7959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project start(mSpannableStringBuilder, new Bold()); 7969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.equalsIgnoreCase("b")) { 7979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project start(mSpannableStringBuilder, new Bold()); 79894d5e9ad6533864f098b99c231d69c77230a828fRomain Guy } else if (tag.equalsIgnoreCase("em")) { 7999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project start(mSpannableStringBuilder, new Italic()); 8009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.equalsIgnoreCase("cite")) { 8019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project start(mSpannableStringBuilder, new Italic()); 8029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.equalsIgnoreCase("dfn")) { 8039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project start(mSpannableStringBuilder, new Italic()); 8049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.equalsIgnoreCase("i")) { 8059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project start(mSpannableStringBuilder, new Italic()); 8069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.equalsIgnoreCase("big")) { 8079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project start(mSpannableStringBuilder, new Big()); 8089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.equalsIgnoreCase("small")) { 8099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project start(mSpannableStringBuilder, new Small()); 8109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.equalsIgnoreCase("font")) { 8119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project startFont(mSpannableStringBuilder, attributes); 8129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.equalsIgnoreCase("blockquote")) { 813cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U startBlockquote(mSpannableStringBuilder, attributes); 8149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.equalsIgnoreCase("tt")) { 8159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project start(mSpannableStringBuilder, new Monospace()); 8169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.equalsIgnoreCase("a")) { 8179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project startA(mSpannableStringBuilder, attributes); 8189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.equalsIgnoreCase("u")) { 8199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project start(mSpannableStringBuilder, new Underline()); 8205e9ed365ade5b0e24c2f6bd61f6dc36fb0f66c0fRoozbeh Pournader } else if (tag.equalsIgnoreCase("del")) { 8215e9ed365ade5b0e24c2f6bd61f6dc36fb0f66c0fRoozbeh Pournader start(mSpannableStringBuilder, new Strikethrough()); 8225e9ed365ade5b0e24c2f6bd61f6dc36fb0f66c0fRoozbeh Pournader } else if (tag.equalsIgnoreCase("s")) { 8235e9ed365ade5b0e24c2f6bd61f6dc36fb0f66c0fRoozbeh Pournader start(mSpannableStringBuilder, new Strikethrough()); 8245e9ed365ade5b0e24c2f6bd61f6dc36fb0f66c0fRoozbeh Pournader } else if (tag.equalsIgnoreCase("strike")) { 8255e9ed365ade5b0e24c2f6bd61f6dc36fb0f66c0fRoozbeh Pournader start(mSpannableStringBuilder, new Strikethrough()); 8269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.equalsIgnoreCase("sup")) { 8279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project start(mSpannableStringBuilder, new Super()); 8289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.equalsIgnoreCase("sub")) { 8299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project start(mSpannableStringBuilder, new Sub()); 8309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.length() == 2 && 831cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U Character.toLowerCase(tag.charAt(0)) == 'h' && 832cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U tag.charAt(1) >= '1' && tag.charAt(1) <= '6') { 833cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U startHeading(mSpannableStringBuilder, attributes, tag.charAt(1) - '1'); 8349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.equalsIgnoreCase("img")) { 8359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project startImg(mSpannableStringBuilder, attributes, mImageGetter); 8369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (mTagHandler != null) { 8379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mTagHandler.handleTag(true, tag, mSpannableStringBuilder, mReader); 8389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void handleEndTag(String tag) { 8429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (tag.equalsIgnoreCase("br")) { 8439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project handleBr(mSpannableStringBuilder); 8449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.equalsIgnoreCase("p")) { 8458b36c0bbd1503c61c111feac939193c47f812190Daniel U endCssStyle(mSpannableStringBuilder); 8468b36c0bbd1503c61c111feac939193c47f812190Daniel U endBlockElement(mSpannableStringBuilder); 8478b36c0bbd1503c61c111feac939193c47f812190Daniel U } else if (tag.equalsIgnoreCase("ul")) { 848cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U endBlockElement(mSpannableStringBuilder); 8498b36c0bbd1503c61c111feac939193c47f812190Daniel U } else if (tag.equalsIgnoreCase("li")) { 8508b36c0bbd1503c61c111feac939193c47f812190Daniel U endLi(mSpannableStringBuilder); 8519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.equalsIgnoreCase("div")) { 852cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U endBlockElement(mSpannableStringBuilder); 8538b36c0bbd1503c61c111feac939193c47f812190Daniel U } else if (tag.equalsIgnoreCase("span")) { 8548b36c0bbd1503c61c111feac939193c47f812190Daniel U endCssStyle(mSpannableStringBuilder); 855dd808c0bd498854e878db257bbf82b73ea5000b4Romain Guy } else if (tag.equalsIgnoreCase("strong")) { 8569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project end(mSpannableStringBuilder, Bold.class, new StyleSpan(Typeface.BOLD)); 8579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.equalsIgnoreCase("b")) { 8589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project end(mSpannableStringBuilder, Bold.class, new StyleSpan(Typeface.BOLD)); 859dd808c0bd498854e878db257bbf82b73ea5000b4Romain Guy } else if (tag.equalsIgnoreCase("em")) { 8609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project end(mSpannableStringBuilder, Italic.class, new StyleSpan(Typeface.ITALIC)); 8619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.equalsIgnoreCase("cite")) { 8629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project end(mSpannableStringBuilder, Italic.class, new StyleSpan(Typeface.ITALIC)); 8639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.equalsIgnoreCase("dfn")) { 8649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project end(mSpannableStringBuilder, Italic.class, new StyleSpan(Typeface.ITALIC)); 8659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.equalsIgnoreCase("i")) { 8669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project end(mSpannableStringBuilder, Italic.class, new StyleSpan(Typeface.ITALIC)); 8679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.equalsIgnoreCase("big")) { 8689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project end(mSpannableStringBuilder, Big.class, new RelativeSizeSpan(1.25f)); 8699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.equalsIgnoreCase("small")) { 8709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project end(mSpannableStringBuilder, Small.class, new RelativeSizeSpan(0.8f)); 8719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.equalsIgnoreCase("font")) { 8729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project endFont(mSpannableStringBuilder); 8739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.equalsIgnoreCase("blockquote")) { 874cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U endBlockquote(mSpannableStringBuilder); 8759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.equalsIgnoreCase("tt")) { 876cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U end(mSpannableStringBuilder, Monospace.class, new TypefaceSpan("monospace")); 8779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.equalsIgnoreCase("a")) { 8789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project endA(mSpannableStringBuilder); 8799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.equalsIgnoreCase("u")) { 8809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project end(mSpannableStringBuilder, Underline.class, new UnderlineSpan()); 8815e9ed365ade5b0e24c2f6bd61f6dc36fb0f66c0fRoozbeh Pournader } else if (tag.equalsIgnoreCase("del")) { 8825e9ed365ade5b0e24c2f6bd61f6dc36fb0f66c0fRoozbeh Pournader end(mSpannableStringBuilder, Strikethrough.class, new StrikethroughSpan()); 8835e9ed365ade5b0e24c2f6bd61f6dc36fb0f66c0fRoozbeh Pournader } else if (tag.equalsIgnoreCase("s")) { 8845e9ed365ade5b0e24c2f6bd61f6dc36fb0f66c0fRoozbeh Pournader end(mSpannableStringBuilder, Strikethrough.class, new StrikethroughSpan()); 8855e9ed365ade5b0e24c2f6bd61f6dc36fb0f66c0fRoozbeh Pournader } else if (tag.equalsIgnoreCase("strike")) { 8865e9ed365ade5b0e24c2f6bd61f6dc36fb0f66c0fRoozbeh Pournader end(mSpannableStringBuilder, Strikethrough.class, new StrikethroughSpan()); 8879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.equalsIgnoreCase("sup")) { 8889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project end(mSpannableStringBuilder, Super.class, new SuperscriptSpan()); 8899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.equalsIgnoreCase("sub")) { 8909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project end(mSpannableStringBuilder, Sub.class, new SubscriptSpan()); 8919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (tag.length() == 2 && 8929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Character.toLowerCase(tag.charAt(0)) == 'h' && 8939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project tag.charAt(1) >= '1' && tag.charAt(1) <= '6') { 894cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U endHeading(mSpannableStringBuilder); 8959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (mTagHandler != null) { 8969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mTagHandler.handleTag(false, tag, mSpannableStringBuilder, mReader); 8979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 8999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 900cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U private int getMarginParagraph() { 901cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U return getMargin(Html.FROM_HTML_SEPARATOR_LINE_BREAK_PARAGRAPH); 902cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U } 9039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 904cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U private int getMarginHeading() { 905cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U return getMargin(Html.FROM_HTML_SEPARATOR_LINE_BREAK_HEADING); 906cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U } 9079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 9088b36c0bbd1503c61c111feac939193c47f812190Daniel U private int getMarginListItem() { 9098b36c0bbd1503c61c111feac939193c47f812190Daniel U return getMargin(Html.FROM_HTML_SEPARATOR_LINE_BREAK_LIST_ITEM); 9108b36c0bbd1503c61c111feac939193c47f812190Daniel U } 9118b36c0bbd1503c61c111feac939193c47f812190Daniel U 9128b36c0bbd1503c61c111feac939193c47f812190Daniel U private int getMarginList() { 9138b36c0bbd1503c61c111feac939193c47f812190Daniel U return getMargin(Html.FROM_HTML_SEPARATOR_LINE_BREAK_LIST); 9148b36c0bbd1503c61c111feac939193c47f812190Daniel U } 9158b36c0bbd1503c61c111feac939193c47f812190Daniel U 916cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U private int getMarginDiv() { 917cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U return getMargin(Html.FROM_HTML_SEPARATOR_LINE_BREAK_DIV); 918cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U } 919cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U 920cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U private int getMarginBlockquote() { 921cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U return getMargin(Html.FROM_HTML_SEPARATOR_LINE_BREAK_BLOCKQUOTE); 922cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U } 923cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U 924cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U /** 925cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U * Returns the minimum number of newline characters needed before and after a given block-level 926cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U * element. 927cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U * 928cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U * @param flag the corresponding option flag defined in {@link Html} of a block-level element 929cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U */ 930cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U private int getMargin(int flag) { 931cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U if ((flag & mFlags) != 0) { 932cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U return 1; 933cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U } 934cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U return 2; 935cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U } 936cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U 937cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U private static void appendNewlines(Editable text, int minNewline) { 938cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U final int len = text.length(); 939cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U 940cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U if (len == 0) { 9419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return; 9429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 944cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U int existingNewlines = 0; 945cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U for (int i = len - 1; i >= 0 && text.charAt(i) == '\n'; i--) { 946cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U existingNewlines++; 947cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U } 948cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U 949cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U for (int j = existingNewlines; j < minNewline; j++) { 950cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U text.append("\n"); 951cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U } 952cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U } 953cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U 954cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U private static void startBlockElement(Editable text, Attributes attributes, int margin) { 955cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U final int len = text.length(); 956cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U if (margin > 0) { 957cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U appendNewlines(text, margin); 9581eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U start(text, new Newline(margin)); 959cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U } 960cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U 961cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U String style = attributes.getValue("", "style"); 962cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U if (style != null) { 963cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U Matcher m = getTextAlignPattern().matcher(style); 964cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U if (m.find()) { 965cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U String alignment = m.group(1); 966cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U if (alignment.equalsIgnoreCase("start")) { 9671eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U start(text, new Alignment(Layout.Alignment.ALIGN_NORMAL)); 968cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U } else if (alignment.equalsIgnoreCase("center")) { 9691eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U start(text, new Alignment(Layout.Alignment.ALIGN_CENTER)); 970cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U } else if (alignment.equalsIgnoreCase("end")) { 9711eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U start(text, new Alignment(Layout.Alignment.ALIGN_OPPOSITE)); 972cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U } 973cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U } 974cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U } 975cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U } 976cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U 977cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U private static void endBlockElement(Editable text) { 978cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U Newline n = getLast(text, Newline.class); 979cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U if (n != null) { 980cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U appendNewlines(text, n.mNumNewlines); 981cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U text.removeSpan(n); 982cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U } 983cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U 984cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U Alignment a = getLast(text, Alignment.class); 985cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U if (a != null) { 986cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U setSpanFromMark(text, a, new AlignmentSpan.Standard(a.mAlignment)); 9879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 9899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 990cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U private static void handleBr(Editable text) { 991cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U text.append('\n'); 992cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U } 993cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U 9948b36c0bbd1503c61c111feac939193c47f812190Daniel U private void startLi(Editable text, Attributes attributes) { 9958b36c0bbd1503c61c111feac939193c47f812190Daniel U startBlockElement(text, attributes, getMarginListItem()); 9968b36c0bbd1503c61c111feac939193c47f812190Daniel U start(text, new Bullet()); 9978b36c0bbd1503c61c111feac939193c47f812190Daniel U startCssStyle(text, attributes); 9988b36c0bbd1503c61c111feac939193c47f812190Daniel U } 9998b36c0bbd1503c61c111feac939193c47f812190Daniel U 10008b36c0bbd1503c61c111feac939193c47f812190Daniel U private static void endLi(Editable text) { 10018b36c0bbd1503c61c111feac939193c47f812190Daniel U endCssStyle(text); 10028b36c0bbd1503c61c111feac939193c47f812190Daniel U endBlockElement(text); 10038b36c0bbd1503c61c111feac939193c47f812190Daniel U end(text, Bullet.class, new BulletSpan()); 10048b36c0bbd1503c61c111feac939193c47f812190Daniel U } 10058b36c0bbd1503c61c111feac939193c47f812190Daniel U 1006cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U private void startBlockquote(Editable text, Attributes attributes) { 1007cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U startBlockElement(text, attributes, getMarginBlockquote()); 1008cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U start(text, new Blockquote()); 1009cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U } 1010cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U 1011cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U private static void endBlockquote(Editable text) { 1012cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U endBlockElement(text); 1013cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U end(text, Blockquote.class, new QuoteSpan()); 10149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1016cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U private void startHeading(Editable text, Attributes attributes, int level) { 1017cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U startBlockElement(text, attributes, getMarginHeading()); 1018cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U start(text, new Heading(level)); 1019cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U } 1020cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U 1021cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U private static void endHeading(Editable text) { 1022cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U // RelativeSizeSpan and StyleSpan are CharacterStyles 1023cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U // Their ranges should not include the newlines at the end 1024cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U Heading h = getLast(text, Heading.class); 1025cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U if (h != null) { 1026cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U setSpanFromMark(text, h, new RelativeSizeSpan(HEADING_SIZES[h.mLevel]), 1027cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U new StyleSpan(Typeface.BOLD)); 1028cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U } 1029cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U 1030cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U endBlockElement(text); 1031cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U } 1032cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U 1033cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U private static <T> T getLast(Spanned text, Class<T> kind) { 10349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* 10359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * This knows that the last returned object from getSpans() 10369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * will be the most recently added. 10379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1038cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U T[] objs = text.getSpans(0, text.length(), kind); 10399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (objs.length == 0) { 10419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return null; 10429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 10439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return objs[objs.length - 1]; 10449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1047cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U private static void setSpanFromMark(Spannable text, Object mark, Object... spans) { 1048cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U int where = text.getSpanStart(mark); 1049cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U text.removeSpan(mark); 1050cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U int len = text.length(); 1051cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U if (where != len) { 1052cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U for (Object span : spans) { 1053cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U text.setSpan(span, where, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 1054cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U } 1055cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U } 1056cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U } 1057cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U 1058cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U private static void start(Editable text, Object mark) { 10599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int len = text.length(); 10601eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U text.setSpan(mark, len, len, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); 10619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1063cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U private static void end(Editable text, Class kind, Object repl) { 10649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int len = text.length(); 10659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Object obj = getLast(text, kind); 1066cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U if (obj != null) { 1067cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U setSpanFromMark(text, obj, repl); 10689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 10709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 10711eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U private void startCssStyle(Editable text, Attributes attributes) { 10728b36c0bbd1503c61c111feac939193c47f812190Daniel U String style = attributes.getValue("", "style"); 10738b36c0bbd1503c61c111feac939193c47f812190Daniel U if (style != null) { 10748b36c0bbd1503c61c111feac939193c47f812190Daniel U Matcher m = getForegroundColorPattern().matcher(style); 10758b36c0bbd1503c61c111feac939193c47f812190Daniel U if (m.find()) { 10761eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U int c = getHtmlColor(m.group(1)); 10778b36c0bbd1503c61c111feac939193c47f812190Daniel U if (c != -1) { 10781eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U start(text, new Foreground(c | 0xFF000000)); 10798b36c0bbd1503c61c111feac939193c47f812190Daniel U } 10808b36c0bbd1503c61c111feac939193c47f812190Daniel U } 10818b36c0bbd1503c61c111feac939193c47f812190Daniel U 10828b36c0bbd1503c61c111feac939193c47f812190Daniel U m = getBackgroundColorPattern().matcher(style); 10838b36c0bbd1503c61c111feac939193c47f812190Daniel U if (m.find()) { 10841eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U int c = getHtmlColor(m.group(1)); 10858b36c0bbd1503c61c111feac939193c47f812190Daniel U if (c != -1) { 10861eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U start(text, new Background(c | 0xFF000000)); 10878b36c0bbd1503c61c111feac939193c47f812190Daniel U } 10888b36c0bbd1503c61c111feac939193c47f812190Daniel U } 10898b36c0bbd1503c61c111feac939193c47f812190Daniel U 10908b36c0bbd1503c61c111feac939193c47f812190Daniel U m = getTextDecorationPattern().matcher(style); 10918b36c0bbd1503c61c111feac939193c47f812190Daniel U if (m.find()) { 10928b36c0bbd1503c61c111feac939193c47f812190Daniel U String textDecoration = m.group(1); 10938b36c0bbd1503c61c111feac939193c47f812190Daniel U if (textDecoration.equalsIgnoreCase("line-through")) { 10941eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U start(text, new Strikethrough()); 10958b36c0bbd1503c61c111feac939193c47f812190Daniel U } 10968b36c0bbd1503c61c111feac939193c47f812190Daniel U } 10978b36c0bbd1503c61c111feac939193c47f812190Daniel U } 10988b36c0bbd1503c61c111feac939193c47f812190Daniel U } 10998b36c0bbd1503c61c111feac939193c47f812190Daniel U 11008b36c0bbd1503c61c111feac939193c47f812190Daniel U private static void endCssStyle(Editable text) { 11018b36c0bbd1503c61c111feac939193c47f812190Daniel U Strikethrough s = getLast(text, Strikethrough.class); 11028b36c0bbd1503c61c111feac939193c47f812190Daniel U if (s != null) { 11038b36c0bbd1503c61c111feac939193c47f812190Daniel U setSpanFromMark(text, s, new StrikethroughSpan()); 11048b36c0bbd1503c61c111feac939193c47f812190Daniel U } 11058b36c0bbd1503c61c111feac939193c47f812190Daniel U 11068b36c0bbd1503c61c111feac939193c47f812190Daniel U Background b = getLast(text, Background.class); 11078b36c0bbd1503c61c111feac939193c47f812190Daniel U if (b != null) { 11088b36c0bbd1503c61c111feac939193c47f812190Daniel U setSpanFromMark(text, b, new BackgroundColorSpan(b.mBackgroundColor)); 11098b36c0bbd1503c61c111feac939193c47f812190Daniel U } 11108b36c0bbd1503c61c111feac939193c47f812190Daniel U 11118b36c0bbd1503c61c111feac939193c47f812190Daniel U Foreground f = getLast(text, Foreground.class); 11128b36c0bbd1503c61c111feac939193c47f812190Daniel U if (f != null) { 11138b36c0bbd1503c61c111feac939193c47f812190Daniel U setSpanFromMark(text, f, new ForegroundColorSpan(f.mForegroundColor)); 11148b36c0bbd1503c61c111feac939193c47f812190Daniel U } 11158b36c0bbd1503c61c111feac939193c47f812190Daniel U } 11168b36c0bbd1503c61c111feac939193c47f812190Daniel U 1117cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U private static void startImg(Editable text, Attributes attributes, Html.ImageGetter img) { 11189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String src = attributes.getValue("", "src"); 11199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Drawable d = null; 11209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (img != null) { 11229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project d = img.getDrawable(src); 11239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (d == null) { 11269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project d = Resources.getSystem(). 11279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project getDrawable(com.android.internal.R.drawable.unknown_image); 11289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); 11299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int len = text.length(); 11329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project text.append("\uFFFC"); 11339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project text.setSpan(new ImageSpan(d, src), len, text.length(), 11359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 11369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11381eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U private void startFont(Editable text, Attributes attributes) { 11399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String color = attributes.getValue("", "color"); 11409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String face = attributes.getValue("", "face"); 11419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11421eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U if (!TextUtils.isEmpty(color)) { 11431eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U int c = getHtmlColor(color); 11441eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U if (c != -1) { 11451eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U start(text, new Foreground(c | 0xFF000000)); 11461eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U } 11471eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U } 11481eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U 11491eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U if (!TextUtils.isEmpty(face)) { 11501eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U start(text, new Font(face)); 11511eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U } 11529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1154cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U private static void endFont(Editable text) { 11551eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U Font font = getLast(text, Font.class); 11561eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U if (font != null) { 11571eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U setSpanFromMark(text, font, new TypefaceSpan(font.mFace)); 11581eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U } 11599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11601eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U Foreground foreground = getLast(text, Foreground.class); 11611eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U if (foreground != null) { 11621eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U setSpanFromMark(text, foreground, 11631eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U new ForegroundColorSpan(foreground.mForegroundColor)); 11649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1167cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U private static void startA(Editable text, Attributes attributes) { 11689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String href = attributes.getValue("", "href"); 11691eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U start(text, new Href(href)); 11709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1172cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U private static void endA(Editable text) { 1173cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U Href h = getLast(text, Href.class); 1174cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U if (h != null) { 11759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (h.mHref != null) { 1176cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U setSpanFromMark(text, h, new URLSpan((h.mHref))); 11779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11811eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U private int getHtmlColor(String color) { 11821eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U if ((mFlags & Html.FROM_HTML_OPTION_USE_CSS_COLORS) 11831eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U == Html.FROM_HTML_OPTION_USE_CSS_COLORS) { 11841eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U Integer i = sColorMap.get(color.toLowerCase(Locale.US)); 11851eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U if (i != null) { 11861eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U return i; 11871eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U } 11881eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U } 11891eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U return Color.getHtmlColor(color); 11901eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U } 11911eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U 11929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void setDocumentLocator(Locator locator) { 11939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void startDocument() throws SAXException { 11969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 11979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void endDocument() throws SAXException { 11999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void startPrefixMapping(String prefix, String uri) throws SAXException { 12029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void endPrefixMapping(String prefix) throws SAXException { 12059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void startElement(String uri, String localName, String qName, Attributes attributes) 12089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project throws SAXException { 12099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project handleStartTag(localName, attributes); 12109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void endElement(String uri, String localName, String qName) throws SAXException { 12139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project handleEndTag(localName); 12149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void characters(char ch[], int start, int length) throws SAXException { 12179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project StringBuilder sb = new StringBuilder(); 12189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /* 12209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Ignore whitespace that immediately follows other whitespace; 12219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * newlines count as spaces. 12229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 12239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = 0; i < length; i++) { 12259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char c = ch[i + start]; 12269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (c == ' ' || c == '\n') { 12289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project char pred; 12299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int len = sb.length(); 12309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (len == 0) { 12329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project len = mSpannableStringBuilder.length(); 12339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (len == 0) { 12359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project pred = '\n'; 12369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 12379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project pred = mSpannableStringBuilder.charAt(len - 1); 12389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 12409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project pred = sb.charAt(len - 1); 12419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (pred != ' ' && pred != '\n') { 12449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project sb.append(' '); 12459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 12479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project sb.append(c); 12489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSpannableStringBuilder.append(sb); 12529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void ignorableWhitespace(char ch[], int start, int length) throws SAXException { 12559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void processingInstruction(String target, String data) throws SAXException { 12589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void skippedEntity(String name) throws SAXException { 12619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static class Bold { } 12649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static class Italic { } 12659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static class Underline { } 12665e9ed365ade5b0e24c2f6bd61f6dc36fb0f66c0fRoozbeh Pournader private static class Strikethrough { } 12679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static class Big { } 12689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static class Small { } 12699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static class Monospace { } 12709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static class Blockquote { } 12719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static class Super { } 12729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static class Sub { } 12738b36c0bbd1503c61c111feac939193c47f812190Daniel U private static class Bullet { } 12749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static class Font { 12769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public String mFace; 12779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12781eab97ac8edec59e3c0453af7c56e1a751e3fa85Daniel U public Font(String face) { 12799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mFace = face; 12809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static class Href { 12849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public String mHref; 12859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public Href(String href) { 12879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mHref = href; 12889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 12909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12918b36c0bbd1503c61c111feac939193c47f812190Daniel U private static class Foreground { 12928b36c0bbd1503c61c111feac939193c47f812190Daniel U private int mForegroundColor; 12938b36c0bbd1503c61c111feac939193c47f812190Daniel U 12948b36c0bbd1503c61c111feac939193c47f812190Daniel U public Foreground(int foregroundColor) { 12958b36c0bbd1503c61c111feac939193c47f812190Daniel U mForegroundColor = foregroundColor; 12968b36c0bbd1503c61c111feac939193c47f812190Daniel U } 12978b36c0bbd1503c61c111feac939193c47f812190Daniel U } 12988b36c0bbd1503c61c111feac939193c47f812190Daniel U 12998b36c0bbd1503c61c111feac939193c47f812190Daniel U private static class Background { 13008b36c0bbd1503c61c111feac939193c47f812190Daniel U private int mBackgroundColor; 13018b36c0bbd1503c61c111feac939193c47f812190Daniel U 13028b36c0bbd1503c61c111feac939193c47f812190Daniel U public Background(int backgroundColor) { 13038b36c0bbd1503c61c111feac939193c47f812190Daniel U mBackgroundColor = backgroundColor; 13048b36c0bbd1503c61c111feac939193c47f812190Daniel U } 13058b36c0bbd1503c61c111feac939193c47f812190Daniel U } 13068b36c0bbd1503c61c111feac939193c47f812190Daniel U 1307cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U private static class Heading { 13089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private int mLevel; 13099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1310cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U public Heading(int level) { 13119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mLevel = level; 13129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 13139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1314cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U 1315cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U private static class Newline { 1316cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U private int mNumNewlines; 1317cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U 1318cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U public Newline(int numNewlines) { 1319cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U mNumNewlines = numNewlines; 1320cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U } 1321cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U } 1322cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U 1323cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U private static class Alignment { 1324cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U private Layout.Alignment mAlignment; 1325cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U 1326cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U public Alignment(Layout.Alignment alignment) { 1327cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U mAlignment = alignment; 1328cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U } 1329cf1fa60331b6ce935788c06fcdfd09817e104c8cDaniel U } 13309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 1331