Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a8f6d5f0 authored by Romain Guy's avatar Romain Guy
Browse files

Add support for face attribute to HTML string resources

Bug #7480719

This change also adds the alias "color" for the attribute "fgcolor".
This change also unifies HTML colors parsing between the Html class
and StringBlock for consistency.

Change-Id: I696a6e080387901d88e9baf7cb989b892f14b9db
parent f6e7b208
Loading
Loading
Loading
Loading
+73 −14
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.content.res;

import android.graphics.Color;
import android.text.*;
import android.text.style.*;
import android.util.Log;
@@ -24,7 +25,7 @@ import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;

import com.android.internal.util.XmlUtils;
import java.util.Arrays;

/**
 * Conveniences for retrieving data out of a compiled string resource.
@@ -33,7 +34,7 @@ import com.android.internal.util.XmlUtils;
 */
final class StringBlock {
    private static final String TAG = "AssetManager";
    private static final boolean localLOGV = false || false;
    private static final boolean localLOGV = false;

    private final int mNative;
    private final boolean mUseSparse;
@@ -82,7 +83,7 @@ final class StringBlock {
            CharSequence res = str;
            int[] style = nativeGetStyle(mNative, idx);
            if (localLOGV) Log.v(TAG, "Got string: " + str);
            if (localLOGV) Log.v(TAG, "Got styles: " + style);
            if (localLOGV) Log.v(TAG, "Got styles: " + Arrays.toString(style));
            if (style != null) {
                if (mStyleIDs == null) {
                    mStyleIDs = new StyleIDs();
@@ -139,10 +140,14 @@ final class StringBlock {
    }

    protected void finalize() throws Throwable {
        try {
            super.finalize();
        } finally {
            if (mOwnsNative) {
                nativeDestroy(mNative);
            }
        }
    }

    static final class StyleIDs {
        private int boldId = -1;
@@ -236,16 +241,28 @@ final class StringBlock {

                    sub = subtag(tag, ";fgcolor=");
                    if (sub != null) {
                        int color = XmlUtils.convertValueToUnsignedInt(sub, -1);
                        buffer.setSpan(new ForegroundColorSpan(color),
                        buffer.setSpan(getColor(sub, true),
                                       style[i+1], style[i+2]+1,
                                       Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                    }

                    sub = subtag(tag, ";color=");
                    if (sub != null) {
                        buffer.setSpan(getColor(sub, true),
                                style[i+1], style[i+2]+1,
                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                    }

                    sub = subtag(tag, ";bgcolor=");
                    if (sub != null) {
                        int color = XmlUtils.convertValueToUnsignedInt(sub, -1);
                        buffer.setSpan(new BackgroundColorSpan(color),
                        buffer.setSpan(getColor(sub, false),
                                       style[i+1], style[i+2]+1,
                                       Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                    }

                    sub = subtag(tag, ";face=");
                    if (sub != null) {
                        buffer.setSpan(new TypefaceSpan(sub),
                                style[i+1], style[i+2]+1,
                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                    }
@@ -288,6 +305,48 @@ final class StringBlock {
        return new SpannedString(buffer);
    }

    /**
     * Returns a span for the specified color string representation.
     * If the specified string does not represent a color (null, empty, etc.)
     * the color black is returned instead.
     *
     * @param color The color as a string. Can be a resource reference,
     *              HTML hexadecimal, octal or a name
     * @param foreground True if the color will be used as the foreground color,
     *                   false otherwise
     *
     * @return A CharacterStyle
     *
     * @see Color#getHtmlColor(String)
     */
    private static CharacterStyle getColor(String color, boolean foreground) {
        int c = 0xff000000;

        if (!TextUtils.isEmpty(color)) {
            if (color.startsWith("@")) {
                Resources res = Resources.getSystem();
                String name = color.substring(1);
                int colorRes = res.getIdentifier(name, "color", "android");
                if (colorRes != 0) {
                    ColorStateList colors = res.getColorStateList(colorRes);
                    if (foreground) {
                        return new TextAppearanceSpan(null, 0, 0, colors, null);
                    } else {
                        c = colors.getDefaultColor();
                    }
                }
            } else {
                c = Color.getHtmlColor(color);
            }
        }

        if (foreground) {
            return new ForegroundColorSpan(c);
        } else {
            return new BackgroundColorSpan(c);
        }
    }

    /**
     * If a translator has messed up the edges of paragraph-level markup,
     * fix it to actually cover the entire paragraph that it is attached to
@@ -423,11 +482,11 @@ final class StringBlock {
                + ": " + nativeGetSize(mNative));
    }

    private static final native int nativeCreate(byte[] data,
    private static native int nativeCreate(byte[] data,
                                                 int offset,
                                                 int size);
    private static final native int nativeGetSize(int obj);
    private static final native String nativeGetString(int obj, int idx);
    private static final native int[] nativeGetStyle(int obj, int idx);
    private static final native void nativeDestroy(int obj);
    private static native int nativeGetSize(int obj);
    private static native String nativeGetString(int obj, int idx);
    private static native int[] nativeGetStyle(int obj, int idx);
    private static native void nativeDestroy(int obj);
}
+7 −51
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.text;

import android.graphics.Color;
import com.android.internal.util.ArrayUtils;
import org.ccil.cowan.tagsoup.HTMLSchema;
import org.ccil.cowan.tagsoup.Parser;
@@ -181,7 +182,7 @@ public class Html {
                }
            }
            if (needDiv) {
                out.append("<div " + elements + ">");
                out.append("<div ").append(elements).append(">");
            }

            withinDiv(out, text, i, next);
@@ -391,7 +392,7 @@ public class Html {
            } else if (c == '&') {
                out.append("&amp;");
            } else if (c > 0x7E || c < ' ') {
                out.append("&#" + ((int) c) + ";");
                out.append("&#").append((int) c).append(";");
            } else if (c == ' ') {
                while (i + 1 < end && text.charAt(i + 1) == ' ') {
                    out.append("&nbsp;");
@@ -616,8 +617,6 @@ class HtmlToSpannedConverter implements ContentHandler {
        if (where != len) {
            text.setSpan(repl, where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        }

        return;
    }

    private static void startImg(SpannableStringBuilder text,
@@ -673,7 +672,7 @@ class HtmlToSpannedConverter implements ContentHandler {
                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                    }
                } else {
                    int c = getHtmlColor(f.mColor);
                    int c = Color.getHtmlColor(f.mColor);
                    if (c != -1) {
                        text.setSpan(new ForegroundColorSpan(c | 0xFF000000),
                                where, len,
@@ -842,47 +841,4 @@ class HtmlToSpannedConverter implements ContentHandler {
            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;
            }
        }
      }

}
+4 −7
Original line number Diff line number Diff line
@@ -123,18 +123,15 @@ public class XmlUtils
        return Integer.parseInt(nm.substring(index), base) * sign;
    }

    public static final int
    convertValueToUnsignedInt(String value, int defaultValue)
    {
        if (null == value)
    public static int convertValueToUnsignedInt(String value, int defaultValue) {
        if (null == value) {
            return defaultValue;
        }

        return parseUnsignedIntAttribute(value);
    }

    public static final int
    parseUnsignedIntAttribute(CharSequence charSeq)
    {
    public static int parseUnsignedIntAttribute(CharSequence charSeq) {
        String  value = charSeq.toString();

        long    bits;
+42 −5
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.graphics;

import android.util.MathUtils;
import com.android.internal.util.XmlUtils;

import java.util.HashMap;
import java.util.Locale;
@@ -200,7 +201,9 @@ public class Color {
     * #RRGGBB
     * #AARRGGBB
     * 'red', 'blue', 'green', 'black', 'white', 'gray', 'cyan', 'magenta',
     * 'yellow', 'lightgray', 'darkgray'
     * 'yellow', 'lightgray', 'darkgray', 'grey', 'lightgrey', 'darkgrey',
     * 'aqua', 'fuschia', 'lime', 'maroon', 'navy', 'olive', 'purple',
     * 'silver', 'teal'
     */
    public static int parseColor(String colorString) {
        if (colorString.charAt(0) == '#') {
@@ -366,6 +369,28 @@ public class Color {
    private static native void nativeRGBToHSV(int red, int greed, int blue, float hsv[]);
    private static native int nativeHSVToColor(int alpha, float hsv[]);

    /**
     * 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.
     *
     * @hide
     */
    public static int getHtmlColor(String color) {
        Integer i = sColorNameMap.get(color.toLowerCase());
        if (i != null) {
            return i;
        } else {
            try {
                return XmlUtils.convertValueToInt(color, -1);
            } catch (NumberFormatException nfe) {
                return -1;
            }
        }
    }

    private static final HashMap<String, Integer> sColorNameMap;

    static {
@@ -381,6 +406,18 @@ public class Color {
        sColorNameMap.put("yellow", YELLOW);
        sColorNameMap.put("cyan", CYAN);
        sColorNameMap.put("magenta", MAGENTA);
        sColorNameMap.put("aqua", 0x00FFFF);
        sColorNameMap.put("fuchsia", 0xFF00FF);
        sColorNameMap.put("darkgrey", DKGRAY);
        sColorNameMap.put("grey", GRAY);
        sColorNameMap.put("lightgrey", LTGRAY);
        sColorNameMap.put("lime", 0x00FF00);
        sColorNameMap.put("maroon", 0x800000);
        sColorNameMap.put("navy", 0x000080);
        sColorNameMap.put("olive", 0x808000);
        sColorNameMap.put("purple", 0x800080);
        sColorNameMap.put("silver", 0xC0C0C0);
        sColorNameMap.put("teal", 0x008080);

    }
}