Allow system colors in <font> tags

Fixes http://b/issue?id=1970693
diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java
index 70e1297..380e5fd 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -25,6 +25,7 @@
 import org.xml.sax.SAXException;
 import org.xml.sax.XMLReader;
 
+import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
@@ -40,6 +41,7 @@
 import android.text.style.StyleSpan;
 import android.text.style.SubscriptSpan;
 import android.text.style.SuperscriptSpan;
+import android.text.style.TextAppearanceSpan;
 import android.text.style.TypefaceSpan;
 import android.text.style.URLSpan;
 import android.text.style.UnderlineSpan;
@@ -49,6 +51,7 @@
 import java.io.IOException;
 import java.io.StringReader;
 import java.nio.CharBuffer;
+import java.util.HashMap;
 
 /**
  * This class processes HTML strings into displayable styled text.
@@ -633,53 +636,24 @@
         if (where != len) {
             Font f = (Font) obj;
 
-            if (f.mColor != null) {
-                int c = -1;
-
-                if (f.mColor.equalsIgnoreCase("aqua")) {
-                    c = 0x00FFFF;
-                } else if (f.mColor.equalsIgnoreCase("black")) {
-                    c = 0x000000;
-                } else if (f.mColor.equalsIgnoreCase("blue")) {
-                    c = 0x0000FF;
-                } else if (f.mColor.equalsIgnoreCase("fuchsia")) {
-                    c = 0xFF00FF;
-                } else if (f.mColor.equalsIgnoreCase("green")) {
-                    c = 0x008000;
-                } else if (f.mColor.equalsIgnoreCase("grey")) {
-                    c = 0x808080;
-                } else if (f.mColor.equalsIgnoreCase("lime")) {
-                    c = 0x00FF00;
-                } else if (f.mColor.equalsIgnoreCase("maroon")) {
-                    c = 0x800000;
-                } else if (f.mColor.equalsIgnoreCase("navy")) {
-                    c = 0x000080;
-                } else if (f.mColor.equalsIgnoreCase("olive")) {
-                    c = 0x808000;
-                } else if (f.mColor.equalsIgnoreCase("purple")) {
-                    c = 0x800080;
-                } else if (f.mColor.equalsIgnoreCase("red")) {
-                    c = 0xFF0000;
-                } else if (f.mColor.equalsIgnoreCase("silver")) {
-                    c = 0xC0C0C0;
-                } else if (f.mColor.equalsIgnoreCase("teal")) {
-                    c = 0x008080;
-                } else if (f.mColor.equalsIgnoreCase("white")) {
-                    c = 0xFFFFFF;
-                } else if (f.mColor.equalsIgnoreCase("yellow")) {
-                    c = 0xFFFF00;
-                } else {
-                    try {
-                        c = XmlUtils.convertValueToInt(f.mColor, -1);
-                    } catch (NumberFormatException nfe) {
-                        // Can't understand the color, so just drop it.
+            if (!TextUtils.isEmpty(f.mColor)) {
+                if (f.mColor.startsWith("@")) {
+                    Resources res = Resources.getSystem();
+                    String name = f.mColor.substring(1);
+                    int colorRes = res.getIdentifier(name, "color", "android");
+                    if (colorRes != 0) {
+                        ColorStateList colors = res.getColorStateList(colorRes);
+                        text.setSpan(new TextAppearanceSpan(null, 0, 0, colors, null),
+                                where, len,
+                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                     }
-                }
-
-                if (c != -1) {
-                    text.setSpan(new ForegroundColorSpan(c | 0xFF000000),
-                                 where, len,
-                                 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                } else {
+                    int c = getHtmlColor(f.mColor);
+                    if (c != -1) {
+                        text.setSpan(new ForegroundColorSpan(c | 0xFF000000),
+                                where, len,
+                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    }
                 }
             }
 
@@ -843,4 +817,47 @@
             mLevel = level;
         }
     }
+
+    private static HashMap<String,Integer> COLORS = buildColorMap();
+
+    private static HashMap<String,Integer> buildColorMap() {
+        HashMap<String,Integer> map = new HashMap<String,Integer>();
+        map.put("aqua", 0x00FFFF);
+        map.put("black", 0x000000);
+        map.put("blue", 0x0000FF);
+        map.put("fuchsia", 0xFF00FF);
+        map.put("green", 0x008000);
+        map.put("grey", 0x808080);
+        map.put("lime", 0x00FF00);
+        map.put("maroon", 0x800000);
+        map.put("navy", 0x000080);
+        map.put("olive", 0x808000);
+        map.put("purple", 0x800080);
+        map.put("red", 0xFF0000);
+        map.put("silver", 0xC0C0C0);
+        map.put("teal", 0x008080);
+        map.put("white", 0xFFFFFF);
+        map.put("yellow", 0xFFFF00);
+        return map;
+    }
+
+    /**
+     * Converts an HTML color (named or numeric) to an integer RGB value.
+     *
+     * @param color Non-null color string.
+     * @return A color value, or {@code -1} if the color string could not be interpreted.
+     */
+    private static int getHtmlColor(String color) {
+        Integer i = COLORS.get(color.toLowerCase());
+        if (i != null) {
+            return i;
+        } else {
+            try {
+                return XmlUtils.convertValueToInt(color, -1);
+            } catch (NumberFormatException nfe) {
+                return -1;
+            }
+        }
+      }
+
 }
diff --git a/tests/AndroidTests/src/com/android/unit_tests/HtmlTest.java b/tests/AndroidTests/src/com/android/unit_tests/HtmlTest.java
index 27da4f1..027730f 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/HtmlTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/HtmlTest.java
@@ -16,11 +16,25 @@
 
 package com.android.unit_tests;
 
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
 import android.graphics.Typeface;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.test.suitebuilder.annotation.SmallTest;
-import android.text.*;
-import android.text.style.*;
+import android.text.Html;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.QuoteSpan;
+import android.text.style.StrikethroughSpan;
+import android.text.style.StyleSpan;
+import android.text.style.SubscriptSpan;
+import android.text.style.SuperscriptSpan;
+import android.text.style.TextAppearanceSpan;
+import android.text.style.TypefaceSpan;
+import android.text.style.URLSpan;
+import android.text.style.UnderlineSpan;
 
 import junit.framework.TestCase;
 
@@ -35,14 +49,54 @@
 
         s = Html.fromHtml("<font color=\"#00FF00\">something</font>");
         colors = s.getSpans(0, s.length(), ForegroundColorSpan.class);
-        assertEquals(colors[0].getForegroundColor(), 0xFF00FF00);
+        assertEquals(1, colors.length);
+        assertEquals(0xFF00FF00, colors[0].getForegroundColor());
 
         s = Html.fromHtml("<font color=\"navy\">something</font>");
         colors = s.getSpans(0, s.length(), ForegroundColorSpan.class);
-        assertEquals(colors[0].getForegroundColor(), 0xFF000080);
+        assertEquals(1, colors.length);
+        assertEquals(0xFF000080, colors[0].getForegroundColor());
 
         s = Html.fromHtml("<font color=\"gibberish\">something</font>");
         colors = s.getSpans(0, s.length(), ForegroundColorSpan.class);
+        assertEquals(0, colors.length);
+    }
+
+    @MediumTest
+    public void testResourceColor() throws Exception {
+        ColorStateList c =
+                Resources.getSystem().getColorStateList(android.R.color.primary_text_dark);
+        Spanned s;
+        TextAppearanceSpan[] colors;
+
+        s = Html.fromHtml("<font color=\"@android:color/primary_text_dark\">something</font>");
+        colors = s.getSpans(0, s.length(), TextAppearanceSpan.class);
+        assertEquals(1, colors.length);
+        assertEquals(c.toString(), colors[0].getTextColor().toString());
+
+        s = Html.fromHtml("<font color=\"@android:primary_text_dark\">something</font>");
+        colors = s.getSpans(0, s.length(), TextAppearanceSpan.class);
+        assertEquals(1, colors.length);
+        assertEquals(c.toString(), colors[0].getTextColor().toString());
+
+        s = Html.fromHtml("<font color=\"@color/primary_text_dark\">something</font>");
+        colors = s.getSpans(0, s.length(), TextAppearanceSpan.class);
+        assertEquals(1, colors.length);
+        assertEquals(c.toString(), colors[0].getTextColor().toString());
+
+        s = Html.fromHtml("<font color=\"@primary_text_dark\">something</font>");
+        colors = s.getSpans(0, s.length(), TextAppearanceSpan.class);
+        assertEquals(1, colors.length);
+        assertEquals(c.toString(), colors[0].getTextColor().toString());
+
+        s = Html.fromHtml("<font color=\"@" + android.R.color.primary_text_dark
+                + "\">something</font>");
+        colors = s.getSpans(0, s.length(), TextAppearanceSpan.class);
+        assertEquals(1, colors.length);
+        assertEquals(c.toString(), colors[0].getTextColor().toString());
+
+        s = Html.fromHtml("<font color=\"gibberish\">something</font>");
+        colors = s.getSpans(0, s.length(), TextAppearanceSpan.class);
         assertEquals(colors.length, 0);
     }