ResourceHelper.java revision 282e181b58cf72b6ca770dc7ca5f91f135444502
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
19282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.ide.common.rendering.api.DensityBasedResourceValue;
20282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.ide.common.rendering.api.LayoutLog;
21282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.ide.common.rendering.api.RenderResources;
22282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.ide.common.rendering.api.ResourceValue;
23282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.layoutlib.bridge.Bridge;
24282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.layoutlib.bridge.android.BridgeContext;
25282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
26282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.ninepatch.NinePatch;
27282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.ninepatch.NinePatchChunk;
28282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport com.android.resources.Density;
29282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
30282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport org.xmlpull.v1.XmlPullParser;
31282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport org.xmlpull.v1.XmlPullParserException;
32282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
33282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.content.res.ColorStateList;
34282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.Bitmap;
35282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.Bitmap_Delegate;
36282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.NinePatch_Delegate;
37282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.Rect;
38282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.drawable.BitmapDrawable;
39282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.drawable.ColorDrawable;
40282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.drawable.Drawable;
41282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.graphics.drawable.NinePatchDrawable;
42282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport android.util.TypedValue;
43282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
44282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.io.File;
45282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.io.FileInputStream;
46282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.io.IOException;
47282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.io.InputStream;
48282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.net.MalformedURLException;
49282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.util.regex.Matcher;
50282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskiimport java.util.regex.Pattern;
51282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
52282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski/**
53282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski * Helper class to provide various conversion method used in handling android resources.
54282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski */
55282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinskipublic final class ResourceHelper {
56282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
57282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private final static Pattern sFloatPattern = Pattern.compile("(-?[0-9]+(?:\\.[0-9]+)?)(.*)");
58282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private final static float[] sFloatOut = new float[1];
59282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
60282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private final static TypedValue mValue = new TypedValue();
61282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
62282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /**
63282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * Returns the color value represented by the given string value
64282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param value the color value
65282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @return the color as an int
66282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @throw NumberFormatException if the conversion failed.
67282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     */
68282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public static int getColor(String value) {
69282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (value != null) {
70282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (value.startsWith("#") == false) {
71282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                throw new NumberFormatException(
72282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        String.format("Color value '%s' must start with #", value));
73282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
74282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
75282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            value = value.substring(1);
76282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
77282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // make sure it's not longer than 32bit
78282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (value.length() > 8) {
79282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                throw new NumberFormatException(String.format(
80282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        "Color value '%s' is too long. Format is either" +
81282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        "#AARRGGBB, #RRGGBB, #RGB, or #ARGB",
82282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        value));
83282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
84282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
85282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (value.length() == 3) { // RGB format
86282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                char[] color = new char[8];
87282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                color[0] = color[1] = 'F';
88282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                color[2] = color[3] = value.charAt(0);
89282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                color[4] = color[5] = value.charAt(1);
90282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                color[6] = color[7] = value.charAt(2);
91282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                value = new String(color);
92282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } else if (value.length() == 4) { // ARGB format
93282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                char[] color = new char[8];
94282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                color[0] = color[1] = value.charAt(0);
95282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                color[2] = color[3] = value.charAt(1);
96282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                color[4] = color[5] = value.charAt(2);
97282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                color[6] = color[7] = value.charAt(3);
98282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                value = new String(color);
99282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } else if (value.length() == 6) {
100282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                value = "FF" + value;
101282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
102282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
103282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // this is a RRGGBB or AARRGGBB value
104282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
105282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // Integer.parseInt will fail to parse strings like "ff191919", so we use
106282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // a Long, but cast the result back into an int, since we know that we're only
107282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // dealing with 32 bit values.
108282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return (int)Long.parseLong(value, 16);
109282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
110282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
111282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        throw new NumberFormatException();
112282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
113282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
114282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public static ColorStateList getColorStateList(ResourceValue resValue, BridgeContext context) {
115282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        String value = resValue.getValue();
116282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (value != null && RenderResources.REFERENCE_NULL.equals(value) == false) {
117282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // first check if the value is a file (xml most likely)
118282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            File f = new File(value);
119282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (f.isFile()) {
120282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                try {
121282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    // let the framework inflate the ColorStateList from the XML file, by
122282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    // providing an XmlPullParser
123282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    XmlPullParser parser = ParserFactory.create(f);
124282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
125282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
126282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                            parser, context, resValue.isFramework());
127282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    try {
128282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        return ColorStateList.createFromXml(context.getResources(), blockParser);
129282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    } finally {
130282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        blockParser.ensurePopped();
131282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    }
132282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                } catch (XmlPullParserException e) {
133282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    Bridge.getLog().error(LayoutLog.TAG_BROKEN,
134282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                            "Failed to configure parser for " + value, e, null /*data*/);
135282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    // we'll return null below.
136282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                } catch (Exception e) {
137282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    // this is an error and not warning since the file existence is
138282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    // checked before attempting to parse it.
139282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
140282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                            "Failed to parse file " + value, e, null /*data*/);
141282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
142282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    return null;
143282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
144282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } else {
145282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                // try to load the color state list from an int
146282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                try {
147282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    int color = ResourceHelper.getColor(value);
148282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    return ColorStateList.valueOf(color);
149282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                } catch (NumberFormatException e) {
150282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
151282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                            "Failed to convert " + value + " into a ColorStateList", e,
152282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                            null /*data*/);
153282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    return null;
154282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
155282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
156282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
157282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
158282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return null;
159282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
160282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
161282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /**
162282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * Returns a drawable from the given value.
163282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param value The value that contains a path to a 9 patch, a bitmap or a xml based drawable,
164282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * or an hexadecimal color
165282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param context the current context
166282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     */
167282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public static Drawable getDrawable(ResourceValue value, BridgeContext context) {
168282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        String stringValue = value.getValue();
169282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (RenderResources.REFERENCE_NULL.equals(stringValue)) {
170282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return null;
171282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
172282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
173282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        String lowerCaseValue = stringValue.toLowerCase();
174282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
175282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        Density density = Density.MEDIUM;
176282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (value instanceof DensityBasedResourceValue) {
177282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            density =
178282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                ((DensityBasedResourceValue)value).getResourceDensity();
179282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
180282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
181282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
182282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (lowerCaseValue.endsWith(NinePatch.EXTENSION_9PATCH)) {
183282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            File file = new File(stringValue);
184282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (file.isFile()) {
185282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                try {
186282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    return getNinePatchDrawable(
187282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                            new FileInputStream(file), density, value.isFramework(),
188282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                            stringValue, context);
189282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                } catch (IOException e) {
190282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    // failed to read the file, we'll return null below.
191282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
192282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                            "Failed lot load " + file.getAbsolutePath(), e, null /*data*/);
193282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
194282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
195282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
196282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return null;
197282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else if (lowerCaseValue.endsWith(".xml")) {
198282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // create a block parser for the file
199282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            File f = new File(stringValue);
200282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (f.isFile()) {
201282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                try {
202282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    // let the framework inflate the Drawable from the XML file.
203282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    XmlPullParser parser = ParserFactory.create(f);
204282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
205282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
206282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                            parser, context, value.isFramework());
207282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    try {
208282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        return Drawable.createFromXml(context.getResources(), blockParser);
209282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    } finally {
210282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        blockParser.ensurePopped();
211282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    }
212282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                } catch (Exception e) {
213282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    // this is an error and not warning since the file existence is checked before
214282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    // attempting to parse it.
215282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    Bridge.getLog().error(null, "Failed to parse file " + stringValue,
216282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                            e, null /*data*/);
217282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
218282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } else {
219282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                Bridge.getLog().error(LayoutLog.TAG_BROKEN,
220282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        String.format("File %s does not exist (or is not a file)", stringValue),
221282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        null /*data*/);
222282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
223282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
224282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return null;
225282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else {
226282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            File bmpFile = new File(stringValue);
227282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (bmpFile.isFile()) {
228282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                try {
229282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    Bitmap bitmap = Bridge.getCachedBitmap(stringValue,
230282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                            value.isFramework() ? null : context.getProjectKey());
231282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
232282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    if (bitmap == null) {
233282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        bitmap = Bitmap_Delegate.createBitmap(bmpFile, false /*isMutable*/,
234282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                density);
235282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        Bridge.setCachedBitmap(stringValue, bitmap,
236282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                value.isFramework() ? null : context.getProjectKey());
237282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    }
238282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
239282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    return new BitmapDrawable(context.getResources(), bitmap);
240282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                } catch (IOException e) {
241282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    // we'll return null below
242282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
243282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                            "Failed lot load " + bmpFile.getAbsolutePath(), e, null /*data*/);
244282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
245282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } else {
246282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                // attempt to get a color from the value
247282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                try {
248282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    int color = getColor(stringValue);
249282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    return new ColorDrawable(color);
250282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                } catch (NumberFormatException e) {
251282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    // we'll return null below.
252282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
253282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                            "Failed to convert " + stringValue + " into a drawable", e,
254282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                            null /*data*/);
255282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
256282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
257282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
258282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
259282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return null;
260282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
261282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
262282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private static Drawable getNinePatchDrawable(InputStream inputStream, Density density,
263282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            boolean isFramework, String cacheKey, BridgeContext context) throws IOException {
264282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // see if we still have both the chunk and the bitmap in the caches
265282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        NinePatchChunk chunk = Bridge.getCached9Patch(cacheKey,
266282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                isFramework ? null : context.getProjectKey());
267282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        Bitmap bitmap = Bridge.getCachedBitmap(cacheKey,
268282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                isFramework ? null : context.getProjectKey());
269282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
270282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // if either chunk or bitmap is null, then we reload the 9-patch file.
271282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (chunk == null || bitmap == null) {
272282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            try {
273282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                NinePatch ninePatch = NinePatch.load(inputStream, true /*is9Patch*/,
274282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        false /* convert */);
275282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (ninePatch != null) {
276282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    if (chunk == null) {
277282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        chunk = ninePatch.getChunk();
278282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
279282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        Bridge.setCached9Patch(cacheKey, chunk,
280282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                isFramework ? null : context.getProjectKey());
281282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    }
282282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
283282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    if (bitmap == null) {
284282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        bitmap = Bitmap_Delegate.createBitmap(ninePatch.getImage(),
285282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                false /*isMutable*/,
286282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                density);
287282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
288282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        Bridge.setCachedBitmap(cacheKey, bitmap,
289282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                isFramework ? null : context.getProjectKey());
290282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    }
291282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
292282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } catch (MalformedURLException e) {
293282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                // URL is wrong, we'll return null below
294282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
295282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
296282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
297282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (chunk != null && bitmap != null) {
298282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            int[] padding = chunk.getPadding();
299282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            Rect paddingRect = new Rect(padding[0], padding[1], padding[2], padding[3]);
300282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
301282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return new NinePatchDrawable(context.getResources(), bitmap,
302282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    NinePatch_Delegate.serialize(chunk),
303282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    paddingRect, null);
304282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
305282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
306282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return null;
307282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
308282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
309282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // ------- TypedValue stuff
310282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    // This is taken from //device/libs/utils/ResourceTypes.cpp
311282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
312282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private static final class UnitEntry {
313282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        String name;
314282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        int type;
315282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        int unit;
316282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        float scale;
317282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
318282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        UnitEntry(String name, int type, int unit, float scale) {
319282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            this.name = name;
320282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            this.type = type;
321282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            this.unit = unit;
322282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            this.scale = scale;
323282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
324282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
325282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
326282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private final static UnitEntry[] sUnitNames = new UnitEntry[] {
327282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        new UnitEntry("px", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_PX, 1.0f),
328282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        new UnitEntry("dip", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_DIP, 1.0f),
329282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        new UnitEntry("dp", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_DIP, 1.0f),
330282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        new UnitEntry("sp", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_SP, 1.0f),
331282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        new UnitEntry("pt", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_PT, 1.0f),
332282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        new UnitEntry("in", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_IN, 1.0f),
333282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        new UnitEntry("mm", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_MM, 1.0f),
334282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        new UnitEntry("%", TypedValue.TYPE_FRACTION, TypedValue.COMPLEX_UNIT_FRACTION, 1.0f/100),
335282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        new UnitEntry("%p", TypedValue.TYPE_FRACTION, TypedValue.COMPLEX_UNIT_FRACTION_PARENT, 1.0f/100),
336282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    };
337282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
338282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /**
339282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * Returns the raw value from the given attribute float-type value string.
340282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * This object is only valid until the next call on to {@link ResourceHelper}.
341282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     */
342282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public static TypedValue getValue(String attribute, String value, boolean requireUnit) {
343282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (parseFloatAttribute(attribute, value, mValue, requireUnit)) {
344282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return mValue;
345282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
346282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
347282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return null;
348282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
349282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
350282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    /**
351282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * Parse a float attribute and return the parsed value into a given TypedValue.
352282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param attribute the name of the attribute. Can be null if <var>requireUnit</var> is false.
353282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param value the string value of the attribute
354282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param outValue the TypedValue to receive the parsed value
355282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @param requireUnit whether the value is expected to contain a unit.
356282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     * @return true if success.
357282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski     */
358282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    public static boolean parseFloatAttribute(String attribute, String value,
359282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            TypedValue outValue, boolean requireUnit) {
360282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        assert requireUnit == false || attribute != null;
361282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
362282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // remove the space before and after
363282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        value = value.trim();
364282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        int len = value.length();
365282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
366282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (len <= 0) {
367282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return false;
368282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
369282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
370282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // check that there's no non ascii characters.
371282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        char[] buf = value.toCharArray();
372282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        for (int i = 0 ; i < len ; i++) {
373282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (buf[i] > 255) {
374282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                return false;
375282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
376282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
377282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
378282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // check the first character
379282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (buf[0] < '0' && buf[0] > '9' && buf[0] != '.' && buf[0] != '-') {
380282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            return false;
381282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
382282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
383282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        // now look for the string that is after the float...
384282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        Matcher m = sFloatPattern.matcher(value);
385282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (m.matches()) {
386282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            String f_str = m.group(1);
387282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            String end = m.group(2);
388282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
389282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            float f;
390282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            try {
391282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                f = Float.parseFloat(f_str);
392282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            } catch (NumberFormatException e) {
393282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                // this shouldn't happen with the regexp above.
394282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                return false;
395282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
396282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
397282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (end.length() > 0 && end.charAt(0) != ' ') {
398282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                // Might be a unit...
399282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (parseUnit(end, outValue, sFloatOut)) {
400282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    computeTypedValue(outValue, f, sFloatOut[0]);
401282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    return true;
402282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
403282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                return false;
404282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
405282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
406282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // make sure it's only spaces at the end.
407282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            end = end.trim();
408282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
409282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (end.length() == 0) {
410282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                if (outValue != null) {
411282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    if (requireUnit == false) {
412282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        outValue.type = TypedValue.TYPE_FLOAT;
413282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        outValue.data = Float.floatToIntBits(f);
414282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    } else {
415282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        // no unit when required? Use dp and out an error.
416282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        applyUnit(sUnitNames[1], outValue, sFloatOut);
417282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        computeTypedValue(outValue, f, sFloatOut[0]);
418282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
419282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                        Bridge.getLog().error(LayoutLog.TAG_RESOURCES_RESOLVE,
420282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                String.format(
421282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                        "Dimension \"%1$s\" in attribute \"%2$s\" is missing unit!",
422282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                        value, attribute),
423282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                                null);
424282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    }
425282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                    return true;
426282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                }
427282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
428282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
429282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
430282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return false;
431282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
432282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
433282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private static void computeTypedValue(TypedValue outValue, float value, float scale) {
434282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        value *= scale;
435282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        boolean neg = value < 0;
436282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (neg) {
437282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            value = -value;
438282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
439282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        long bits = (long)(value*(1<<23)+.5f);
440282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        int radix;
441282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        int shift;
442282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if ((bits&0x7fffff) == 0) {
443282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // Always use 23p0 if there is no fraction, just to make
444282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // things easier to read.
445282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            radix = TypedValue.COMPLEX_RADIX_23p0;
446282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            shift = 23;
447282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else if ((bits&0xffffffffff800000L) == 0) {
448282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // Magnitude is zero -- can fit in 0 bits of precision.
449282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            radix = TypedValue.COMPLEX_RADIX_0p23;
450282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            shift = 0;
451282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else if ((bits&0xffffffff80000000L) == 0) {
452282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // Magnitude can fit in 8 bits of precision.
453282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            radix = TypedValue.COMPLEX_RADIX_8p15;
454282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            shift = 8;
455282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else if ((bits&0xffffff8000000000L) == 0) {
456282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // Magnitude can fit in 16 bits of precision.
457282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            radix = TypedValue.COMPLEX_RADIX_16p7;
458282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            shift = 16;
459282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        } else {
460282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            // Magnitude needs entire range, so no fractional part.
461282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            radix = TypedValue.COMPLEX_RADIX_23p0;
462282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            shift = 23;
463282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
464282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        int mantissa = (int)(
465282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            (bits>>shift) & TypedValue.COMPLEX_MANTISSA_MASK);
466282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        if (neg) {
467282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            mantissa = (-mantissa) & TypedValue.COMPLEX_MANTISSA_MASK;
468282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
469282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        outValue.data |=
470282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            (radix<<TypedValue.COMPLEX_RADIX_SHIFT)
471282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            | (mantissa<<TypedValue.COMPLEX_MANTISSA_SHIFT);
472282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
473282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
474282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private static boolean parseUnit(String str, TypedValue outValue, float[] outScale) {
475282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        str = str.trim();
476282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
477282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        for (UnitEntry unit : sUnitNames) {
478282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            if (unit.name.equals(str)) {
479282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                applyUnit(unit, outValue, outScale);
480282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski                return true;
481282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski            }
482282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        }
483282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
484282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        return false;
485282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
486282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
487282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    private static void applyUnit(UnitEntry unit, TypedValue outValue, float[] outScale) {
488282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        outValue.type = unit.type;
489282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        outValue.data = unit.unit << TypedValue.COMPLEX_UNIT_SHIFT;
490282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski        outScale[0] = unit.scale;
491282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski    }
492282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski}
493282e181b58cf72b6ca770dc7ca5f91f135444502Adam Lesinski
494