1282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski/*
2282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Copyright (C) 2008 The Android Open Source Project
3282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
4282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Licensed under the Apache License, Version 2.0 (the "License");
5282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * you may not use this file except in compliance with the License.
6282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * You may obtain a copy of the License at
7282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
8282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *      http://www.apache.org/licenses/LICENSE-2.0
9282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski *
10282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Unless required by applicable law or agreed to in writing, software
11282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * distributed under the License is distributed on an "AS IS" BASIS,
12282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * See the License for the specific language governing permissions and
14282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * limitations under the License.
15282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */
16282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
17282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskipackage com.android.layoutlib.bridge.impl;
18282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
195f1bf496948fb6383ae17e5467a26a00489d7564Deepanshu Guptaimport com.android.SdkConstants;
20282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.ide.common.rendering.api.DensityBasedResourceValue;
21282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.ide.common.rendering.api.LayoutLog;
22282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.ide.common.rendering.api.RenderResources;
23282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.ide.common.rendering.api.ResourceValue;
24fc8f4aad7395eca11f6d9b82eb266b1f4ee5041bDeepanshu Guptaimport com.android.internal.util.XmlUtils;
25282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.layoutlib.bridge.Bridge;
26282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.layoutlib.bridge.android.BridgeContext;
27282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
28cd6e47e4455cec8daa4570ac8073d93bd567d139Deepanshu Guptaimport com.android.layoutlib.bridge.android.RenderParamsFlags;
29282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.ninepatch.NinePatch;
30282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.ninepatch.NinePatchChunk;
31282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.resources.Density;
32282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
33282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport org.xmlpull.v1.XmlPullParser;
34282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport org.xmlpull.v1.XmlPullParserException;
35282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
36476e582d2ffdf25102d4c55f8c242baa3d21d37fDeepanshu Guptaimport android.annotation.NonNull;
37566b303365078fac9a454f1595add19e02631db3Diego Perezimport android.annotation.Nullable;
38282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.content.res.ColorStateList;
39566b303365078fac9a454f1595add19e02631db3Diego Perezimport android.content.res.ComplexColor;
40566b303365078fac9a454f1595add19e02631db3Diego Perezimport android.content.res.ComplexColor_Accessor;
41789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perezimport android.content.res.FontResourcesParser;
42566b303365078fac9a454f1595add19e02631db3Diego Perezimport android.content.res.GradientColor;
434a605c6fa0027ee116beb29fbc9625721f0441f0Deepanshu Guptaimport android.content.res.Resources.Theme;
44282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.Bitmap;
45282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.Bitmap_Delegate;
46d88c717b4e124e435e54bf1542774aa100773c3eDiego Perezimport android.graphics.Color;
47282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.NinePatch_Delegate;
48282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.Rect;
49789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perezimport android.graphics.Typeface;
50789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perezimport android.graphics.Typeface_Accessor;
51282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.drawable.BitmapDrawable;
52282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.drawable.ColorDrawable;
53282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.drawable.Drawable;
54282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.drawable.NinePatchDrawable;
55789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perezimport android.text.FontConfig;
56282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.util.TypedValue;
57282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
58282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.io.File;
59282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.io.FileInputStream;
60566b303365078fac9a454f1595add19e02631db3Diego Perezimport java.io.FileNotFoundException;
61282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.io.IOException;
62282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.io.InputStream;
63282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.net.MalformedURLException;
64282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.util.regex.Matcher;
65282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.util.regex.Pattern;
66282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
67282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski/**
68282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Helper class to provide various conversion method used in handling android resources.
69282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */
70282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskipublic final class ResourceHelper {
71282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
72282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private final static Pattern sFloatPattern = Pattern.compile("(-?[0-9]+(?:\\.[0-9]+)?)(.*)");
73282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private final static float[] sFloatOut = new float[1];
74282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
75282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private final static TypedValue mValue = new TypedValue();
76282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
77282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /**
78282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * Returns the color value represented by the given string value
79282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param value the color value
80282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @return the color as an int
811a12b805698c7cba9c2daa78c76c055afbdfa9fcDeepanshu Gupta     * @throws NumberFormatException if the conversion failed.
82282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     */
83282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public static int getColor(String value) {
84282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (value != null) {
85fb175664469f1eb1bc598f54a7f86ff008e88b0eDiego Perez            value = value.trim();
861a12b805698c7cba9c2daa78c76c055afbdfa9fcDeepanshu Gupta            if (!value.startsWith("#")) {
875f1bf496948fb6383ae17e5467a26a00489d7564Deepanshu Gupta                if (value.startsWith(SdkConstants.PREFIX_THEME_REF)) {
885f1bf496948fb6383ae17e5467a26a00489d7564Deepanshu Gupta                    throw new NumberFormatException(String.format(
895f1bf496948fb6383ae17e5467a26a00489d7564Deepanshu Gupta                            "Attribute '%s' not found. Are you using the right theme?", value));
905f1bf496948fb6383ae17e5467a26a00489d7564Deepanshu Gupta                }
91282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                throw new NumberFormatException(
92282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        String.format("Color value '%s' must start with #", value));
93282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
94282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
95282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            value = value.substring(1);
96282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
97282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // make sure it's not longer than 32bit
98282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (value.length() > 8) {
99282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                throw new NumberFormatException(String.format(
100282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        "Color value '%s' is too long. Format is either" +
101282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        "#AARRGGBB, #RRGGBB, #RGB, or #ARGB",
102282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        value));
103282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
104282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
105282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (value.length() == 3) { // RGB format
106282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                char[] color = new char[8];
107282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                color[0] = color[1] = 'F';
108282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                color[2] = color[3] = value.charAt(0);
109282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                color[4] = color[5] = value.charAt(1);
110282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                color[6] = color[7] = value.charAt(2);
111282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                value = new String(color);
112282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } else if (value.length() == 4) { // ARGB format
113282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                char[] color = new char[8];
114282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                color[0] = color[1] = value.charAt(0);
115282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                color[2] = color[3] = value.charAt(1);
116282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                color[4] = color[5] = value.charAt(2);
117282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                color[6] = color[7] = value.charAt(3);
118282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                value = new String(color);
119282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } else if (value.length() == 6) {
120282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                value = "FF" + value;
121282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
122282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
123282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // this is a RRGGBB or AARRGGBB value
124282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
125282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // Integer.parseInt will fail to parse strings like "ff191919", so we use
126282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // a Long, but cast the result back into an int, since we know that we're only
127282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // dealing with 32 bit values.
128282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return (int)Long.parseLong(value, 16);
129282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
130282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
131282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        throw new NumberFormatException();
132282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
133282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
134566b303365078fac9a454f1595add19e02631db3Diego Perez    /**
135566b303365078fac9a454f1595add19e02631db3Diego Perez     * Returns a {@link ComplexColor} from the given {@link ResourceValue}
136566b303365078fac9a454f1595add19e02631db3Diego Perez     *
137566b303365078fac9a454f1595add19e02631db3Diego Perez     * @param resValue the value containing a color value or a file path to a complex color
138566b303365078fac9a454f1595add19e02631db3Diego Perez     * definition
139566b303365078fac9a454f1595add19e02631db3Diego Perez     * @param context the current context
140566b303365078fac9a454f1595add19e02631db3Diego Perez     * @param theme the theme to use when resolving the complex color
141566b303365078fac9a454f1595add19e02631db3Diego Perez     * @param allowGradients when false, only {@link ColorStateList} will be returned. If a {@link
142566b303365078fac9a454f1595add19e02631db3Diego Perez     * GradientColor} is found, null will be returned.
143566b303365078fac9a454f1595add19e02631db3Diego Perez     */
144566b303365078fac9a454f1595add19e02631db3Diego Perez    @Nullable
145566b303365078fac9a454f1595add19e02631db3Diego Perez    private static ComplexColor getInternalComplexColor(@NonNull ResourceValue resValue,
146566b303365078fac9a454f1595add19e02631db3Diego Perez            @NonNull BridgeContext context, @Nullable Theme theme, boolean allowGradients) {
147282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        String value = resValue.getValue();
148566b303365078fac9a454f1595add19e02631db3Diego Perez        if (value == null || RenderResources.REFERENCE_NULL.equals(value)) {
149566b303365078fac9a454f1595add19e02631db3Diego Perez            return null;
150566b303365078fac9a454f1595add19e02631db3Diego Perez        }
151566b303365078fac9a454f1595add19e02631db3Diego Perez
152cd6e47e4455cec8daa4570ac8073d93bd567d139Deepanshu Gupta        XmlPullParser parser = null;
153566b303365078fac9a454f1595add19e02631db3Diego Perez        // first check if the value is a file (xml most likely)
154cd6e47e4455cec8daa4570ac8073d93bd567d139Deepanshu Gupta        Boolean psiParserSupport = context.getLayoutlibCallback().getFlag(
155cd6e47e4455cec8daa4570ac8073d93bd567d139Deepanshu Gupta                RenderParamsFlags.FLAG_KEY_XML_FILE_PARSER_SUPPORT);
156cd6e47e4455cec8daa4570ac8073d93bd567d139Deepanshu Gupta        if (psiParserSupport != null && psiParserSupport) {
157cd6e47e4455cec8daa4570ac8073d93bd567d139Deepanshu Gupta            parser = context.getLayoutlibCallback().getXmlFileParser(value);
158cd6e47e4455cec8daa4570ac8073d93bd567d139Deepanshu Gupta        }
159566b303365078fac9a454f1595add19e02631db3Diego Perez        if (parser == null) {
160282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            File f = new File(value);
161282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (f.isFile()) {
162566b303365078fac9a454f1595add19e02631db3Diego Perez                // let the framework inflate the color from the XML file, by
163566b303365078fac9a454f1595add19e02631db3Diego Perez                // providing an XmlPullParser
164282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                try {
165566b303365078fac9a454f1595add19e02631db3Diego Perez                    parser = ParserFactory.create(f);
166566b303365078fac9a454f1595add19e02631db3Diego Perez                } catch (XmlPullParserException | FileNotFoundException e) {
167282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
168282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                            "Failed to parse file " + value, e, null /*data*/);
169282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
170566b303365078fac9a454f1595add19e02631db3Diego Perez            }
171566b303365078fac9a454f1595add19e02631db3Diego Perez        }
172566b303365078fac9a454f1595add19e02631db3Diego Perez
173566b303365078fac9a454f1595add19e02631db3Diego Perez        if (parser != null) {
174566b303365078fac9a454f1595add19e02631db3Diego Perez            try {
175566b303365078fac9a454f1595add19e02631db3Diego Perez                BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
176566b303365078fac9a454f1595add19e02631db3Diego Perez                        parser, context, resValue.isFramework());
177282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                try {
178566b303365078fac9a454f1595add19e02631db3Diego Perez                    // Advance the parser to the first element so we can detect if it's a
179566b303365078fac9a454f1595add19e02631db3Diego Perez                    // color list or a gradient color
180566b303365078fac9a454f1595add19e02631db3Diego Perez                    int type;
181566b303365078fac9a454f1595add19e02631db3Diego Perez                    //noinspection StatementWithEmptyBody
182566b303365078fac9a454f1595add19e02631db3Diego Perez                    while ((type = blockParser.next()) != XmlPullParser.START_TAG
183566b303365078fac9a454f1595add19e02631db3Diego Perez                            && type != XmlPullParser.END_DOCUMENT) {
184566b303365078fac9a454f1595add19e02631db3Diego Perez                        // Seek parser to start tag.
185566b303365078fac9a454f1595add19e02631db3Diego Perez                    }
186566b303365078fac9a454f1595add19e02631db3Diego Perez
187566b303365078fac9a454f1595add19e02631db3Diego Perez                    if (type != XmlPullParser.START_TAG) {
188d88c717b4e124e435e54bf1542774aa100773c3eDiego Perez                        assert false : "No start tag found";
189d88c717b4e124e435e54bf1542774aa100773c3eDiego Perez                        return null;
190566b303365078fac9a454f1595add19e02631db3Diego Perez                    }
191566b303365078fac9a454f1595add19e02631db3Diego Perez
192566b303365078fac9a454f1595add19e02631db3Diego Perez                    final String name = blockParser.getName();
193566b303365078fac9a454f1595add19e02631db3Diego Perez                    if (allowGradients && "gradient".equals(name)) {
194566b303365078fac9a454f1595add19e02631db3Diego Perez                        return ComplexColor_Accessor.createGradientColorFromXmlInner(
195566b303365078fac9a454f1595add19e02631db3Diego Perez                                context.getResources(),
196566b303365078fac9a454f1595add19e02631db3Diego Perez                                blockParser, blockParser,
197566b303365078fac9a454f1595add19e02631db3Diego Perez                                theme);
198566b303365078fac9a454f1595add19e02631db3Diego Perez                    } else if ("selector".equals(name)) {
199566b303365078fac9a454f1595add19e02631db3Diego Perez                        return ComplexColor_Accessor.createColorStateListFromXmlInner(
200566b303365078fac9a454f1595add19e02631db3Diego Perez                                context.getResources(),
201566b303365078fac9a454f1595add19e02631db3Diego Perez                                blockParser, blockParser,
202566b303365078fac9a454f1595add19e02631db3Diego Perez                                theme);
203566b303365078fac9a454f1595add19e02631db3Diego Perez                    }
204566b303365078fac9a454f1595add19e02631db3Diego Perez                } finally {
205566b303365078fac9a454f1595add19e02631db3Diego Perez                    blockParser.ensurePopped();
206282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
207566b303365078fac9a454f1595add19e02631db3Diego Perez            } catch (XmlPullParserException e) {
208566b303365078fac9a454f1595add19e02631db3Diego Perez                Bridge.getLog().error(LayoutLog.TAG_BROKEN,
209566b303365078fac9a454f1595add19e02631db3Diego Perez                        "Failed to configure parser for " + value, e, null /*data*/);
210566b303365078fac9a454f1595add19e02631db3Diego Perez                // we'll return null below.
211566b303365078fac9a454f1595add19e02631db3Diego Perez            } catch (Exception e) {
212566b303365078fac9a454f1595add19e02631db3Diego Perez                // this is an error and not warning since the file existence is
213566b303365078fac9a454f1595add19e02631db3Diego Perez                // checked before attempting to parse it.
214566b303365078fac9a454f1595add19e02631db3Diego Perez                Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
215566b303365078fac9a454f1595add19e02631db3Diego Perez                        "Failed to parse file " + value, e, null /*data*/);
216566b303365078fac9a454f1595add19e02631db3Diego Perez
217566b303365078fac9a454f1595add19e02631db3Diego Perez                return null;
218566b303365078fac9a454f1595add19e02631db3Diego Perez            }
219566b303365078fac9a454f1595add19e02631db3Diego Perez        } else {
220566b303365078fac9a454f1595add19e02631db3Diego Perez            // try to load the color state list from an int
221566b303365078fac9a454f1595add19e02631db3Diego Perez            try {
222566b303365078fac9a454f1595add19e02631db3Diego Perez                int color = getColor(value);
223566b303365078fac9a454f1595add19e02631db3Diego Perez                return ColorStateList.valueOf(color);
224566b303365078fac9a454f1595add19e02631db3Diego Perez            } catch (NumberFormatException e) {
225566b303365078fac9a454f1595add19e02631db3Diego Perez                Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
226566b303365078fac9a454f1595add19e02631db3Diego Perez                        "Failed to convert " + value + " into a ColorStateList", e,
227566b303365078fac9a454f1595add19e02631db3Diego Perez                        null /*data*/);
228282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
229282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
230282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
231282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return null;
232282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
233282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
234282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /**
235566b303365078fac9a454f1595add19e02631db3Diego Perez     * Returns a {@link ColorStateList} from the given {@link ResourceValue}
236566b303365078fac9a454f1595add19e02631db3Diego Perez     *
237566b303365078fac9a454f1595add19e02631db3Diego Perez     * @param resValue the value containing a color value or a file path to a complex color
238566b303365078fac9a454f1595add19e02631db3Diego Perez     * definition
239566b303365078fac9a454f1595add19e02631db3Diego Perez     * @param context the current context
240566b303365078fac9a454f1595add19e02631db3Diego Perez     */
241566b303365078fac9a454f1595add19e02631db3Diego Perez    @Nullable
242566b303365078fac9a454f1595add19e02631db3Diego Perez    public static ColorStateList getColorStateList(@NonNull ResourceValue resValue,
243566b303365078fac9a454f1595add19e02631db3Diego Perez            @NonNull BridgeContext context) {
244566b303365078fac9a454f1595add19e02631db3Diego Perez        return (ColorStateList) getInternalComplexColor(resValue, context, context.getTheme(),
245566b303365078fac9a454f1595add19e02631db3Diego Perez                false);
246566b303365078fac9a454f1595add19e02631db3Diego Perez    }
247566b303365078fac9a454f1595add19e02631db3Diego Perez
248566b303365078fac9a454f1595add19e02631db3Diego Perez    /**
249566b303365078fac9a454f1595add19e02631db3Diego Perez     * Returns a {@link ComplexColor} from the given {@link ResourceValue}
250566b303365078fac9a454f1595add19e02631db3Diego Perez     *
251566b303365078fac9a454f1595add19e02631db3Diego Perez     * @param resValue the value containing a color value or a file path to a complex color
252566b303365078fac9a454f1595add19e02631db3Diego Perez     * definition
253566b303365078fac9a454f1595add19e02631db3Diego Perez     * @param context the current context
254566b303365078fac9a454f1595add19e02631db3Diego Perez     */
255566b303365078fac9a454f1595add19e02631db3Diego Perez    @Nullable
256566b303365078fac9a454f1595add19e02631db3Diego Perez    public static ComplexColor getComplexColor(@NonNull ResourceValue resValue,
257566b303365078fac9a454f1595add19e02631db3Diego Perez            @NonNull BridgeContext context) {
258566b303365078fac9a454f1595add19e02631db3Diego Perez        return getInternalComplexColor(resValue, context, context.getTheme(), true);
259566b303365078fac9a454f1595add19e02631db3Diego Perez    }
260566b303365078fac9a454f1595add19e02631db3Diego Perez
261566b303365078fac9a454f1595add19e02631db3Diego Perez    /**
262282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * Returns a drawable from the given value.
263282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param value The value that contains a path to a 9 patch, a bitmap or a xml based drawable,
264282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * or an hexadecimal color
265282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param context the current context
266282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     */
267282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public static Drawable getDrawable(ResourceValue value, BridgeContext context) {
2684a605c6fa0027ee116beb29fbc9625721f0441f0Deepanshu Gupta        return getDrawable(value, context, null);
2694a605c6fa0027ee116beb29fbc9625721f0441f0Deepanshu Gupta    }
2704a605c6fa0027ee116beb29fbc9625721f0441f0Deepanshu Gupta
2714a605c6fa0027ee116beb29fbc9625721f0441f0Deepanshu Gupta    /**
2724a605c6fa0027ee116beb29fbc9625721f0441f0Deepanshu Gupta     * Returns a drawable from the given value.
2734a605c6fa0027ee116beb29fbc9625721f0441f0Deepanshu Gupta     * @param value The value that contains a path to a 9 patch, a bitmap or a xml based drawable,
2744a605c6fa0027ee116beb29fbc9625721f0441f0Deepanshu Gupta     * or an hexadecimal color
2754a605c6fa0027ee116beb29fbc9625721f0441f0Deepanshu Gupta     * @param context the current context
2764a605c6fa0027ee116beb29fbc9625721f0441f0Deepanshu Gupta     * @param theme the theme to be used to inflate the drawable.
2774a605c6fa0027ee116beb29fbc9625721f0441f0Deepanshu Gupta     */
2784a605c6fa0027ee116beb29fbc9625721f0441f0Deepanshu Gupta    public static Drawable getDrawable(ResourceValue value, BridgeContext context, Theme theme) {
27914bf0cef7eeed572a67c29a328581afac4decc20Deepanshu Gupta        if (value == null) {
28014bf0cef7eeed572a67c29a328581afac4decc20Deepanshu Gupta            return null;
28114bf0cef7eeed572a67c29a328581afac4decc20Deepanshu Gupta        }
282282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        String stringValue = value.getValue();
283282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (RenderResources.REFERENCE_NULL.equals(stringValue)) {
284282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return null;
285282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
286282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
287282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        String lowerCaseValue = stringValue.toLowerCase();
288282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
289282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        Density density = Density.MEDIUM;
290282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (value instanceof DensityBasedResourceValue) {
291bc9d2a13f0bbeae4ebf04da5a0b0d2f7814b40e4Diego Perez            density = ((DensityBasedResourceValue) value).getResourceDensity();
292282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
293282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
294282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (lowerCaseValue.endsWith(NinePatch.EXTENSION_9PATCH)) {
295282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            File file = new File(stringValue);
296282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (file.isFile()) {
297282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                try {
298bc9d2a13f0bbeae4ebf04da5a0b0d2f7814b40e4Diego Perez                    return getNinePatchDrawable(new FileInputStream(file), density,
299bc9d2a13f0bbeae4ebf04da5a0b0d2f7814b40e4Diego Perez                            value.isFramework(), stringValue, context);
300282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                } catch (IOException e) {
301282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    // failed to read the file, we'll return null below.
302282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
303282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                            "Failed lot load " + file.getAbsolutePath(), e, null /*data*/);
304282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
305282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
306282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
307282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return null;
308bc9d2a13f0bbeae4ebf04da5a0b0d2f7814b40e4Diego Perez        } else if (lowerCaseValue.endsWith(".xml") || stringValue.startsWith("@aapt:_aapt/")) {
309282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // create a block parser for the file
310bc9d2a13f0bbeae4ebf04da5a0b0d2f7814b40e4Diego Perez            try {
311bc9d2a13f0bbeae4ebf04da5a0b0d2f7814b40e4Diego Perez                XmlPullParser parser = context.getLayoutlibCallback().getParser(value);
312bc9d2a13f0bbeae4ebf04da5a0b0d2f7814b40e4Diego Perez                if (parser == null) {
313bc9d2a13f0bbeae4ebf04da5a0b0d2f7814b40e4Diego Perez                    File drawableFile = new File(stringValue);
314bc9d2a13f0bbeae4ebf04da5a0b0d2f7814b40e4Diego Perez                    if (drawableFile.isFile()) {
315bc9d2a13f0bbeae4ebf04da5a0b0d2f7814b40e4Diego Perez                        parser = ParserFactory.create(drawableFile);
316282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    }
317282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
318bc9d2a13f0bbeae4ebf04da5a0b0d2f7814b40e4Diego Perez
319bc9d2a13f0bbeae4ebf04da5a0b0d2f7814b40e4Diego Perez                BridgeXmlBlockParser blockParser =
320bc9d2a13f0bbeae4ebf04da5a0b0d2f7814b40e4Diego Perez                        new BridgeXmlBlockParser(parser, context, value.isFramework());
321bc9d2a13f0bbeae4ebf04da5a0b0d2f7814b40e4Diego Perez                try {
322bc9d2a13f0bbeae4ebf04da5a0b0d2f7814b40e4Diego Perez                    return Drawable.createFromXml(context.getResources(), blockParser, theme);
323bc9d2a13f0bbeae4ebf04da5a0b0d2f7814b40e4Diego Perez                } finally {
324bc9d2a13f0bbeae4ebf04da5a0b0d2f7814b40e4Diego Perez                    blockParser.ensurePopped();
325bc9d2a13f0bbeae4ebf04da5a0b0d2f7814b40e4Diego Perez                }
326bc9d2a13f0bbeae4ebf04da5a0b0d2f7814b40e4Diego Perez            } catch (Exception e) {
327bc9d2a13f0bbeae4ebf04da5a0b0d2f7814b40e4Diego Perez                // this is an error and not warning since the file existence is checked before
328bc9d2a13f0bbeae4ebf04da5a0b0d2f7814b40e4Diego Perez                // attempting to parse it.
329bc9d2a13f0bbeae4ebf04da5a0b0d2f7814b40e4Diego Perez                Bridge.getLog().error(null, "Failed to parse file " + stringValue, e,
330282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        null /*data*/);
331282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
332282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
333282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return null;
334282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else {
335282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            File bmpFile = new File(stringValue);
336282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (bmpFile.isFile()) {
337282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                try {
338282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    Bitmap bitmap = Bridge.getCachedBitmap(stringValue,
339282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                            value.isFramework() ? null : context.getProjectKey());
340282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
341282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    if (bitmap == null) {
342bc9d2a13f0bbeae4ebf04da5a0b0d2f7814b40e4Diego Perez                        bitmap =
343bc9d2a13f0bbeae4ebf04da5a0b0d2f7814b40e4Diego Perez                                Bitmap_Delegate.createBitmap(bmpFile, false /*isMutable*/, density);
344282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        Bridge.setCachedBitmap(stringValue, bitmap,
345282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                value.isFramework() ? null : context.getProjectKey());
346282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    }
347282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
348282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    return new BitmapDrawable(context.getResources(), bitmap);
349282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                } catch (IOException e) {
350282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    // we'll return null below
351282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
352282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                            "Failed lot load " + bmpFile.getAbsolutePath(), e, null /*data*/);
353282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
354282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } else {
355282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                // attempt to get a color from the value
356282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                try {
357282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    int color = getColor(stringValue);
358282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    return new ColorDrawable(color);
359282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                } catch (NumberFormatException e) {
360282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    // we'll return null below.
361282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
362282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                            "Failed to convert " + stringValue + " into a drawable", e,
363282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                            null /*data*/);
364282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
365282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
366282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
367282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
368282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return null;
369282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
370282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
371789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez    /**
372789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez     * Returns a {@link Typeface} given a font name. The font name, can be a system font family
373789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez     * (like sans-serif) or a full path if the font is to be loaded from resources.
374789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez     */
375789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez    public static Typeface getFont(String fontName, BridgeContext context, Theme theme, boolean
376789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez            isFramework) {
377789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez        if (fontName == null) {
378789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez            return null;
379789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez        }
380789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez
381789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez        if (Typeface_Accessor.isSystemFont(fontName)) {
382789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez            // Shortcut for the case where we are asking for a system font name. Those are not
383789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez            // loaded using external resources.
384789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez            return null;
385789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez        }
386789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez
387789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez        // Check if this is an asset that we've already loaded dynamically
388789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez        Typeface typeface = Typeface.findFromCache(context.getAssets(), fontName);
389789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez        if (typeface != null) {
390789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez            return typeface;
391789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez        }
392789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez
393789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez        String lowerCaseValue = fontName.toLowerCase();
394789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez        if (lowerCaseValue.endsWith(".xml")) {
395789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez            // create a block parser for the file
396789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez            Boolean psiParserSupport = context.getLayoutlibCallback().getFlag(
397789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez                    RenderParamsFlags.FLAG_KEY_XML_FILE_PARSER_SUPPORT);
398789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez            XmlPullParser parser = null;
399789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez            if (psiParserSupport != null && psiParserSupport) {
400789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez                parser = context.getLayoutlibCallback().getXmlFileParser(fontName);
401789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez            }
402789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez            else {
403789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez                File f = new File(fontName);
404789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez                if (f.isFile()) {
405789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez                    try {
406789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez                        parser = ParserFactory.create(f);
407789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez                    } catch (XmlPullParserException | FileNotFoundException e) {
408789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez                        // this is an error and not warning since the file existence is checked before
409789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez                        // attempting to parse it.
410789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez                        Bridge.getLog().error(null, "Failed to parse file " + fontName,
411789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez                                e, null /*data*/);
412789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez                    }
413789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez                }
414789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez            }
415789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez
416789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez            if (parser != null) {
417789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez                BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
418789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez                        parser, context, isFramework);
419789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez                try {
420ac873c9f25d2a687c9195226b9d680f51c91fa30Seigo Nonaka                    FontResourcesParser.FamilyResourceEntry entry =
421ac873c9f25d2a687c9195226b9d680f51c91fa30Seigo Nonaka                            FontResourcesParser.parse(blockParser, context.getResources());
422ac873c9f25d2a687c9195226b9d680f51c91fa30Seigo Nonaka                    typeface = Typeface.createFromResources(entry, context.getAssets(),
423789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez                            fontName);
424789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez                } catch (XmlPullParserException | IOException e) {
425789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez                    Bridge.getLog().error(null, "Failed to parse file " + fontName,
426789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez                            e, null /*data*/);
427789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez                } finally {
428789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez                    blockParser.ensurePopped();
429789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez                }
430789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez            } else {
431789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez                Bridge.getLog().error(LayoutLog.TAG_BROKEN,
432789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez                        String.format("File %s does not exist (or is not a file)", fontName),
433789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez                        null /*data*/);
434789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez            }
435789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez        } else {
436789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez            typeface = Typeface.createFromResources(context.getAssets(), fontName, 0);
437789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez        }
438789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez
439789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez        return typeface;
440789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez    }
441789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez
442789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez    /**
443789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez     * Returns a {@link Typeface} given a font name. The font name, can be a system font family
444789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez     * (like sans-serif) or a full path if the font is to be loaded from resources.
445789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez     */
446789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez    public static Typeface getFont(ResourceValue value, BridgeContext context, Theme theme) {
447789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez        if (value == null) {
448789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez            return null;
449789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez        }
450789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez
451789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez        return getFont(value.getValue(), context, theme, value.isFramework());
452789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez    }
453789c4b4b14880621f05e7750f594b24bc93fcff9Diego Perez
454282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private static Drawable getNinePatchDrawable(InputStream inputStream, Density density,
455282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            boolean isFramework, String cacheKey, BridgeContext context) throws IOException {
456282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // see if we still have both the chunk and the bitmap in the caches
457282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        NinePatchChunk chunk = Bridge.getCached9Patch(cacheKey,
458282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                isFramework ? null : context.getProjectKey());
459282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        Bitmap bitmap = Bridge.getCachedBitmap(cacheKey,
460282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                isFramework ? null : context.getProjectKey());
461282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
462282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // if either chunk or bitmap is null, then we reload the 9-patch file.
463282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (chunk == null || bitmap == null) {
464282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            try {
465282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                NinePatch ninePatch = NinePatch.load(inputStream, true /*is9Patch*/,
466282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        false /* convert */);
467282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (ninePatch != null) {
468282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    if (chunk == null) {
469282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        chunk = ninePatch.getChunk();
470282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
471282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        Bridge.setCached9Patch(cacheKey, chunk,
472282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                isFramework ? null : context.getProjectKey());
473282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    }
474282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
475282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    if (bitmap == null) {
476282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        bitmap = Bitmap_Delegate.createBitmap(ninePatch.getImage(),
477282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                false /*isMutable*/,
478282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                density);
479282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
480282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        Bridge.setCachedBitmap(cacheKey, bitmap,
481282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                isFramework ? null : context.getProjectKey());
482282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    }
483282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
484282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } catch (MalformedURLException e) {
485282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                // URL is wrong, we'll return null below
486282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
487282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
488282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
489282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (chunk != null && bitmap != null) {
490282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            int[] padding = chunk.getPadding();
491282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            Rect paddingRect = new Rect(padding[0], padding[1], padding[2], padding[3]);
492282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
493282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return new NinePatchDrawable(context.getResources(), bitmap,
494282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    NinePatch_Delegate.serialize(chunk),
495282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    paddingRect, null);
496282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
497282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
498282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return null;
499282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
500282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
501fc8f4aad7395eca11f6d9b82eb266b1f4ee5041bDeepanshu Gupta    /**
502fc8f4aad7395eca11f6d9b82eb266b1f4ee5041bDeepanshu Gupta     * Looks for an attribute in the current theme.
503fc8f4aad7395eca11f6d9b82eb266b1f4ee5041bDeepanshu Gupta     *
504fc8f4aad7395eca11f6d9b82eb266b1f4ee5041bDeepanshu Gupta     * @param resources the render resources
505fc8f4aad7395eca11f6d9b82eb266b1f4ee5041bDeepanshu Gupta     * @param name the name of the attribute
506fc8f4aad7395eca11f6d9b82eb266b1f4ee5041bDeepanshu Gupta     * @param defaultValue the default value.
507fc8f4aad7395eca11f6d9b82eb266b1f4ee5041bDeepanshu Gupta     * @param isFrameworkAttr if the attribute is in android namespace
508fc8f4aad7395eca11f6d9b82eb266b1f4ee5041bDeepanshu Gupta     * @return the value of the attribute or the default one if not found.
509fc8f4aad7395eca11f6d9b82eb266b1f4ee5041bDeepanshu Gupta     */
510fc8f4aad7395eca11f6d9b82eb266b1f4ee5041bDeepanshu Gupta    public static boolean getBooleanThemeValue(@NonNull RenderResources resources, String name,
511fc8f4aad7395eca11f6d9b82eb266b1f4ee5041bDeepanshu Gupta            boolean isFrameworkAttr, boolean defaultValue) {
512fc8f4aad7395eca11f6d9b82eb266b1f4ee5041bDeepanshu Gupta        ResourceValue value = resources.findItemInTheme(name, isFrameworkAttr);
513fc8f4aad7395eca11f6d9b82eb266b1f4ee5041bDeepanshu Gupta        value = resources.resolveResValue(value);
514fc8f4aad7395eca11f6d9b82eb266b1f4ee5041bDeepanshu Gupta        if (value == null) {
515fc8f4aad7395eca11f6d9b82eb266b1f4ee5041bDeepanshu Gupta            return defaultValue;
516fc8f4aad7395eca11f6d9b82eb266b1f4ee5041bDeepanshu Gupta        }
517fc8f4aad7395eca11f6d9b82eb266b1f4ee5041bDeepanshu Gupta        return XmlUtils.convertValueToBoolean(value.getValue(), defaultValue);
518fc8f4aad7395eca11f6d9b82eb266b1f4ee5041bDeepanshu Gupta    }
519fc8f4aad7395eca11f6d9b82eb266b1f4ee5041bDeepanshu Gupta
520282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // ------- TypedValue stuff
521282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // This is taken from //device/libs/utils/ResourceTypes.cpp
522282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
523282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private static final class UnitEntry {
524282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        String name;
525282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        int type;
526282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        int unit;
527282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        float scale;
528282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
529282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        UnitEntry(String name, int type, int unit, float scale) {
530282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            this.name = name;
531282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            this.type = type;
532282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            this.unit = unit;
533282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            this.scale = scale;
534282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
535282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
536282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
537282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private final static UnitEntry[] sUnitNames = new UnitEntry[] {
538282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        new UnitEntry("px", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_PX, 1.0f),
539282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        new UnitEntry("dip", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_DIP, 1.0f),
540282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        new UnitEntry("dp", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_DIP, 1.0f),
541282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        new UnitEntry("sp", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_SP, 1.0f),
542282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        new UnitEntry("pt", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_PT, 1.0f),
543282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        new UnitEntry("in", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_IN, 1.0f),
544282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        new UnitEntry("mm", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_MM, 1.0f),
545282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        new UnitEntry("%", TypedValue.TYPE_FRACTION, TypedValue.COMPLEX_UNIT_FRACTION, 1.0f/100),
546282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        new UnitEntry("%p", TypedValue.TYPE_FRACTION, TypedValue.COMPLEX_UNIT_FRACTION_PARENT, 1.0f/100),
547282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    };
548282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
549282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /**
550282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * Returns the raw value from the given attribute float-type value string.
551282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * This object is only valid until the next call on to {@link ResourceHelper}.
552282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     */
553282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public static TypedValue getValue(String attribute, String value, boolean requireUnit) {
554282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (parseFloatAttribute(attribute, value, mValue, requireUnit)) {
555282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return mValue;
556282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
557282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
558282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return null;
559282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
560282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
561282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /**
562282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * Parse a float attribute and return the parsed value into a given TypedValue.
563282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param attribute the name of the attribute. Can be null if <var>requireUnit</var> is false.
564282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param value the string value of the attribute
565282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param outValue the TypedValue to receive the parsed value
566282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param requireUnit whether the value is expected to contain a unit.
567282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @return true if success.
568282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     */
569171804201dd556f8ce7ee7618661a5c8ee71507aDeepanshu Gupta    public static boolean parseFloatAttribute(String attribute, @NonNull String value,
570282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            TypedValue outValue, boolean requireUnit) {
5711a12b805698c7cba9c2daa78c76c055afbdfa9fcDeepanshu Gupta        assert !requireUnit || attribute != null;
572282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
573282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // remove the space before and after
574282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        value = value.trim();
575282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        int len = value.length();
576282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
577282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (len <= 0) {
578282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return false;
579282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
580282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
581282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // check that there's no non ascii characters.
582282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        char[] buf = value.toCharArray();
583282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        for (int i = 0 ; i < len ; i++) {
584282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (buf[i] > 255) {
585282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                return false;
586282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
587282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
588282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
589282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // check the first character
590458f2a7bbbcc45d12b575fe9f3b2f8e1a20f9b1cDeepanshu Gupta        if ((buf[0] < '0' || buf[0] > '9') && buf[0] != '.' && buf[0] != '-' && buf[0] != '+') {
591282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return false;
592282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
593282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
594282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // now look for the string that is after the float...
595282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        Matcher m = sFloatPattern.matcher(value);
596282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (m.matches()) {
597282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            String f_str = m.group(1);
598282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            String end = m.group(2);
599282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
600282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            float f;
601282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            try {
602282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                f = Float.parseFloat(f_str);
603282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } catch (NumberFormatException e) {
604282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                // this shouldn't happen with the regexp above.
605282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                return false;
606282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
607282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
608282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (end.length() > 0 && end.charAt(0) != ' ') {
609282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                // Might be a unit...
610282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (parseUnit(end, outValue, sFloatOut)) {
611282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    computeTypedValue(outValue, f, sFloatOut[0]);
612282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    return true;
613282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
614282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                return false;
615282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
616282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
617282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // make sure it's only spaces at the end.
618282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            end = end.trim();
619282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
620282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (end.length() == 0) {
621282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (outValue != null) {
6221a12b805698c7cba9c2daa78c76c055afbdfa9fcDeepanshu Gupta                    if (!requireUnit) {
623282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        outValue.type = TypedValue.TYPE_FLOAT;
624282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        outValue.data = Float.floatToIntBits(f);
625282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    } else {
626282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        // no unit when required? Use dp and out an error.
627282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        applyUnit(sUnitNames[1], outValue, sFloatOut);
628282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        computeTypedValue(outValue, f, sFloatOut[0]);
629282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
630282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        Bridge.getLog().error(LayoutLog.TAG_RESOURCES_RESOLVE,
631282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                String.format(
632282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                        "Dimension \"%1$s\" in attribute \"%2$s\" is missing unit!",
633282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                        value, attribute),
634282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                null);
635282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    }
636282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    return true;
637282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
638282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
639282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
640282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
641282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return false;
642282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
643282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
644282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private static void computeTypedValue(TypedValue outValue, float value, float scale) {
645282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        value *= scale;
646282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        boolean neg = value < 0;
647282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (neg) {
648282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            value = -value;
649282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
650282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        long bits = (long)(value*(1<<23)+.5f);
651282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        int radix;
652282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        int shift;
653282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if ((bits&0x7fffff) == 0) {
654282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // Always use 23p0 if there is no fraction, just to make
655282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // things easier to read.
656282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            radix = TypedValue.COMPLEX_RADIX_23p0;
657282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            shift = 23;
658282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else if ((bits&0xffffffffff800000L) == 0) {
659282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // Magnitude is zero -- can fit in 0 bits of precision.
660282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            radix = TypedValue.COMPLEX_RADIX_0p23;
661282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            shift = 0;
662282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else if ((bits&0xffffffff80000000L) == 0) {
663282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // Magnitude can fit in 8 bits of precision.
664282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            radix = TypedValue.COMPLEX_RADIX_8p15;
665282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            shift = 8;
666282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else if ((bits&0xffffff8000000000L) == 0) {
667282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // Magnitude can fit in 16 bits of precision.
668282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            radix = TypedValue.COMPLEX_RADIX_16p7;
669282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            shift = 16;
670282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else {
671282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // Magnitude needs entire range, so no fractional part.
672282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            radix = TypedValue.COMPLEX_RADIX_23p0;
673282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            shift = 23;
674282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
675282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        int mantissa = (int)(
676282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            (bits>>shift) & TypedValue.COMPLEX_MANTISSA_MASK);
677282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (neg) {
678282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mantissa = (-mantissa) & TypedValue.COMPLEX_MANTISSA_MASK;
679282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
680282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        outValue.data |=
681282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            (radix<<TypedValue.COMPLEX_RADIX_SHIFT)
682282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            | (mantissa<<TypedValue.COMPLEX_MANTISSA_SHIFT);
683282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
684282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
685282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private static boolean parseUnit(String str, TypedValue outValue, float[] outScale) {
686282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        str = str.trim();
687282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
688282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        for (UnitEntry unit : sUnitNames) {
689282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (unit.name.equals(str)) {
690282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                applyUnit(unit, outValue, outScale);
691282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                return true;
692282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
693282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
694282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
695282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return false;
696282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
697282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
698282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private static void applyUnit(UnitEntry unit, TypedValue outValue, float[] outScale) {
699282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        outValue.type = unit.type;
7001a12b805698c7cba9c2daa78c76c055afbdfa9fcDeepanshu Gupta        // COMPLEX_UNIT_SHIFT is 0 and hence intelliJ complains about it. Suppress the warning.
7011a12b805698c7cba9c2daa78c76c055afbdfa9fcDeepanshu Gupta        //noinspection PointlessBitwiseExpression
702282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        outValue.data = unit.unit << TypedValue.COMPLEX_UNIT_SHIFT;
703282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        outScale[0] = unit.scale;
704282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
705282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
706282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
707