19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17c2e9651bf386a1f7bf7fc706cf5424950570470cXavier Ducrohetpackage com.android.layoutlib.bridge.impl;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1919a021038f2f4683dddef651543d7298f5bd7218Xavier Ducrohetimport com.android.ide.common.rendering.api.DensityBasedResourceValue;
20918aaa5717fce6081557c82ce1c439b6922737d5Xavier Ducrohetimport com.android.ide.common.rendering.api.LayoutLog;
21bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohetimport com.android.ide.common.rendering.api.RenderResources;
2219a021038f2f4683dddef651543d7298f5bd7218Xavier Ducrohetimport com.android.ide.common.rendering.api.ResourceValue;
23c2e9651bf386a1f7bf7fc706cf5424950570470cXavier Ducrohetimport com.android.layoutlib.bridge.Bridge;
24c2e9651bf386a1f7bf7fc706cf5424950570470cXavier Ducrohetimport com.android.layoutlib.bridge.android.BridgeContext;
25c2e9651bf386a1f7bf7fc706cf5424950570470cXavier Ducrohetimport com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport com.android.ninepatch.NinePatch;
272d56b273ef6e2984a4e8914fb67772b173d0a154Xavier Ducrohetimport com.android.ninepatch.NinePatchChunk;
2816584225125acba18b74920b902c798dfead0328Xavier Ducrohetimport com.android.resources.Density;
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport org.xmlpull.v1.XmlPullParser;
31bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohetimport org.xmlpull.v1.XmlPullParserException;
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
33bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohetimport android.content.res.ColorStateList;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Bitmap;
355de11a18e9151e6bc9b3e81cf31fc43dc63dffbfXavier Ducrohetimport android.graphics.Bitmap_Delegate;
362d56b273ef6e2984a4e8914fb67772b173d0a154Xavier Ducrohetimport android.graphics.NinePatch_Delegate;
372d56b273ef6e2984a4e8914fb67772b173d0a154Xavier Ducrohetimport android.graphics.Rect;
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.drawable.BitmapDrawable;
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.drawable.ColorDrawable;
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.drawable.Drawable;
412d56b273ef6e2984a4e8914fb67772b173d0a154Xavier Ducrohetimport android.graphics.drawable.NinePatchDrawable;
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.TypedValue;
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.File;
4513bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohetimport java.io.FileInputStream;
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.IOException;
4713bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohetimport java.io.InputStream;
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.net.MalformedURLException;
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.regex.Matcher;
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.regex.Pattern;
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
5390c6b7e639921e264ac65699439578bcbdbf583aXavier Ducrohet * Helper class to provide various conversion method used in handling android resources.
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic final class ResourceHelper {
5656a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private final static Pattern sFloatPattern = Pattern.compile("(-?[0-9]+(?:\\.[0-9]+)?)(.*)");
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private final static float[] sFloatOut = new float[1];
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private final static TypedValue mValue = new TypedValue();
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns the color value represented by the given string value
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param value the color value
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the color as an int
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @throw NumberFormatException if the conversion failed.
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
68c2e9651bf386a1f7bf7fc706cf5424950570470cXavier Ducrohet    public static int getColor(String value) {
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (value != null) {
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (value.startsWith("#") == false) {
7156222cfbe9973c518f7e8c9113c614de80b5a4b2Xavier Ducrohet                throw new NumberFormatException(
7256222cfbe9973c518f7e8c9113c614de80b5a4b2Xavier Ducrohet                        String.format("Color value '%s' must start with #", value));
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            value = value.substring(1);
7656a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // make sure it's not longer than 32bit
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (value.length() > 8) {
7956222cfbe9973c518f7e8c9113c614de80b5a4b2Xavier Ducrohet                throw new NumberFormatException(String.format(
8056222cfbe9973c518f7e8c9113c614de80b5a4b2Xavier Ducrohet                        "Color value '%s' is too long. Format is either" +
8156222cfbe9973c518f7e8c9113c614de80b5a4b2Xavier Ducrohet                        "#AARRGGBB, #RRGGBB, #RGB, or #ARGB",
8256222cfbe9973c518f7e8c9113c614de80b5a4b2Xavier Ducrohet                        value));
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8456a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (value.length() == 3) { // RGB format
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                char[] color = new char[8];
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                color[0] = color[1] = 'F';
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                color[2] = color[3] = value.charAt(0);
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                color[4] = color[5] = value.charAt(1);
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                color[6] = color[7] = value.charAt(2);
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                value = new String(color);
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (value.length() == 4) { // ARGB format
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                char[] color = new char[8];
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                color[0] = color[1] = value.charAt(0);
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                color[2] = color[3] = value.charAt(1);
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                color[4] = color[5] = value.charAt(2);
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                color[6] = color[7] = value.charAt(3);
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                value = new String(color);
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (value.length() == 6) {
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                value = "FF" + value;
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // this is a RRGGBB or AARRGGBB value
10456a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Integer.parseInt will fail to parse strings like "ff191919", so we use
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // a Long, but cast the result back into an int, since we know that we're only
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // dealing with 32 bit values.
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return (int)Long.parseLong(value, 16);
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        throw new NumberFormatException();
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
114bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet    public static ColorStateList getColorStateList(ResourceValue resValue, BridgeContext context) {
115bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet        String value = resValue.getValue();
1162fae858db55fc6984ef923a6226b9408c37c72cbXavier Ducrohet        if (value != null && RenderResources.REFERENCE_NULL.equals(value) == false) {
117bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet            // first check if the value is a file (xml most likely)
118bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet            File f = new File(value);
119bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet            if (f.isFile()) {
120bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet                try {
121bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet                    // let the framework inflate the ColorStateList from the XML file, by
122bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet                    // providing an XmlPullParser
12304ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet                    XmlPullParser parser = ParserFactory.create(f);
124bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet
12502d2b5a4031c80bfe1012ce2f4f7b3695762abd9Xavier Ducrohet                    BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
12602d2b5a4031c80bfe1012ce2f4f7b3695762abd9Xavier Ducrohet                            parser, context, resValue.isFramework());
12702d2b5a4031c80bfe1012ce2f4f7b3695762abd9Xavier Ducrohet                    try {
12802d2b5a4031c80bfe1012ce2f4f7b3695762abd9Xavier Ducrohet                        return ColorStateList.createFromXml(context.getResources(), blockParser);
12902d2b5a4031c80bfe1012ce2f4f7b3695762abd9Xavier Ducrohet                    } finally {
13002d2b5a4031c80bfe1012ce2f4f7b3695762abd9Xavier Ducrohet                        blockParser.ensurePopped();
13102d2b5a4031c80bfe1012ce2f4f7b3695762abd9Xavier Ducrohet                    }
132bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet                } catch (XmlPullParserException e) {
133bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet                    Bridge.getLog().error(LayoutLog.TAG_BROKEN,
134bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet                            "Failed to configure parser for " + value, e, null /*data*/);
135bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet                    // we'll return null below.
136bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet                } catch (Exception e) {
137bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet                    // this is an error and not warning since the file existence is
138bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet                    // checked before attempting to parse it.
139bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet                    Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
140bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet                            "Failed to parse file " + value, e, null /*data*/);
141bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet
142bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet                    return null;
143bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet                }
144bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet            } else {
145bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet                // try to load the color state list from an int
146bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet                try {
147bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet                    int color = ResourceHelper.getColor(value);
148bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet                    return ColorStateList.valueOf(color);
149bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet                } catch (NumberFormatException e) {
150bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet                    Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
151bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet                            "Failed to convert " + value + " into a ColorStateList", e,
152bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet                            null /*data*/);
153bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet                    return null;
154bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet                }
155bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet            }
156bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet        }
157bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet
158bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet        return null;
159bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet    }
160bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns a drawable from the given value.
16356a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet     * @param value The value that contains a path to a 9 patch, a bitmap or a xml based drawable,
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * or an hexadecimal color
165bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet     * @param context the current context
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
167bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet    public static Drawable getDrawable(ResourceValue value, BridgeContext context) {
16856a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet        String stringValue = value.getValue();
169bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet        if (RenderResources.REFERENCE_NULL.equals(stringValue)) {
170bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet            return null;
171bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet        }
17256a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet
17356a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet        String lowerCaseValue = stringValue.toLowerCase();
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17513bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet        Density density = Density.MEDIUM;
17613bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet        if (value instanceof DensityBasedResourceValue) {
17713bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet            density =
17813bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet                ((DensityBasedResourceValue)value).getResourceDensity();
17913bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet        }
18013bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet
18113bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (lowerCaseValue.endsWith(NinePatch.EXTENSION_9PATCH)) {
18356a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet            File file = new File(stringValue);
18456a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet            if (file.isFile()) {
18513bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet                try {
18613bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet                    return getNinePatchDrawable(
18713bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet                            new FileInputStream(file), density, value.isFramework(),
18813bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet                            stringValue, context);
18913bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet                } catch (IOException e) {
19013bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet                    // failed to read the file, we'll return null below.
19113bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet                    Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
19213bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet                            "Failed lot load " + file.getAbsolutePath(), e, null /*data*/);
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
19556a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else if (lowerCaseValue.endsWith(".xml")) {
19890c6b7e639921e264ac65699439578bcbdbf583aXavier Ducrohet            // create a block parser for the file
19956a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet            File f = new File(stringValue);
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (f.isFile()) {
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                try {
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // let the framework inflate the Drawable from the XML file.
20304ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet                    XmlPullParser parser = ParserFactory.create(f);
20456a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet
20502d2b5a4031c80bfe1012ce2f4f7b3695762abd9Xavier Ducrohet                    BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
20602d2b5a4031c80bfe1012ce2f4f7b3695762abd9Xavier Ducrohet                            parser, context, value.isFramework());
20702d2b5a4031c80bfe1012ce2f4f7b3695762abd9Xavier Ducrohet                    try {
20802d2b5a4031c80bfe1012ce2f4f7b3695762abd9Xavier Ducrohet                        return Drawable.createFromXml(context.getResources(), blockParser);
20902d2b5a4031c80bfe1012ce2f4f7b3695762abd9Xavier Ducrohet                    } finally {
21002d2b5a4031c80bfe1012ce2f4f7b3695762abd9Xavier Ducrohet                        blockParser.ensurePopped();
21102d2b5a4031c80bfe1012ce2f4f7b3695762abd9Xavier Ducrohet                    }
2126c740cf71e5ce5ba7c8493c545c3a57c57ac7024Xavier Ducrohet                } catch (Exception e) {
2136c740cf71e5ce5ba7c8493c545c3a57c57ac7024Xavier Ducrohet                    // this is an error and not warning since the file existence is checked before
2146c740cf71e5ce5ba7c8493c545c3a57c57ac7024Xavier Ducrohet                    // attempting to parse it.
215b353495192ba1acce94b8ab8aeeffe3c9a3bcfacXavier Ducrohet                    Bridge.getLog().error(null, "Failed to parse file " + stringValue,
216b353495192ba1acce94b8ab8aeeffe3c9a3bcfacXavier Ducrohet                            e, null /*data*/);
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2186c740cf71e5ce5ba7c8493c545c3a57c57ac7024Xavier Ducrohet            } else {
219918aaa5717fce6081557c82ce1c439b6922737d5Xavier Ducrohet                Bridge.getLog().error(LayoutLog.TAG_BROKEN,
22051a7e5447de94791c464cda5cc6ebbf616d73c80Xavier Ducrohet                        String.format("File %s does not exist (or is not a file)", stringValue),
22151a7e5447de94791c464cda5cc6ebbf616d73c80Xavier Ducrohet                        null /*data*/);
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
22656a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet            File bmpFile = new File(stringValue);
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (bmpFile.isFile()) {
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                try {
22956a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet                    Bitmap bitmap = Bridge.getCachedBitmap(stringValue,
230bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet                            value.isFramework() ? null : context.getProjectKey());
23156a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (bitmap == null) {
2332d56b273ef6e2984a4e8914fb67772b173d0a154Xavier Ducrohet                        bitmap = Bitmap_Delegate.createBitmap(bmpFile, false /*isMutable*/,
2342d56b273ef6e2984a4e8914fb67772b173d0a154Xavier Ducrohet                                density);
2352d56b273ef6e2984a4e8914fb67772b173d0a154Xavier Ducrohet                        Bridge.setCachedBitmap(stringValue, bitmap,
236bbbb8326020368958a3f1d248878329e9d6b10c0Xavier Ducrohet                                value.isFramework() ? null : context.getProjectKey());
23756a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet                    }
23856a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet
23956a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet                    return new BitmapDrawable(context.getResources(), bitmap);
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } catch (IOException e) {
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // we'll return null below
242918aaa5717fce6081557c82ce1c439b6922737d5Xavier Ducrohet                    Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
24351a7e5447de94791c464cda5cc6ebbf616d73c80Xavier Ducrohet                            "Failed lot load " + bmpFile.getAbsolutePath(), e, null /*data*/);
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // attempt to get a color from the value
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                try {
24856a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet                    int color = getColor(stringValue);
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return new ColorDrawable(color);
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } catch (NumberFormatException e) {
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // we'll return null below.
252918aaa5717fce6081557c82ce1c439b6922737d5Xavier Ducrohet                    Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT,
25351a7e5447de94791c464cda5cc6ebbf616d73c80Xavier Ducrohet                            "Failed to convert " + stringValue + " into a drawable", e,
25451a7e5447de94791c464cda5cc6ebbf616d73c80Xavier Ducrohet                            null /*data*/);
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
25856a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return null;
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
26213bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet    private static Drawable getNinePatchDrawable(InputStream inputStream, Density density,
26313bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet            boolean isFramework, String cacheKey, BridgeContext context) throws IOException {
26413bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet        // see if we still have both the chunk and the bitmap in the caches
26513bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet        NinePatchChunk chunk = Bridge.getCached9Patch(cacheKey,
26613bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet                isFramework ? null : context.getProjectKey());
26713bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet        Bitmap bitmap = Bridge.getCachedBitmap(cacheKey,
26813bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet                isFramework ? null : context.getProjectKey());
26913bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet
27013bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet        // if either chunk or bitmap is null, then we reload the 9-patch file.
27113bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet        if (chunk == null || bitmap == null) {
27213bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet            try {
27313bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet                NinePatch ninePatch = NinePatch.load(inputStream, true /*is9Patch*/,
27413bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet                        false /* convert */);
27513bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet                if (ninePatch != null) {
27613bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet                    if (chunk == null) {
27713bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet                        chunk = ninePatch.getChunk();
27813bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet
27913bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet                        Bridge.setCached9Patch(cacheKey, chunk,
28013bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet                                isFramework ? null : context.getProjectKey());
28113bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet                    }
28213bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet
28313bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet                    if (bitmap == null) {
28413bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet                        bitmap = Bitmap_Delegate.createBitmap(ninePatch.getImage(),
28513bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet                                false /*isMutable*/,
28613bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet                                density);
28713bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet
28813bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet                        Bridge.setCachedBitmap(cacheKey, bitmap,
28913bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet                                isFramework ? null : context.getProjectKey());
29013bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet                    }
29113bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet                }
29213bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet            } catch (MalformedURLException e) {
29313bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet                // URL is wrong, we'll return null below
29413bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet            }
29513bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet        }
29613bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet
29713bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet        if (chunk != null && bitmap != null) {
29813bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet            int[] padding = chunk.getPadding();
29913bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet            Rect paddingRect = new Rect(padding[0], padding[1], padding[2], padding[3]);
30013bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet
30113bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet            return new NinePatchDrawable(context.getResources(), bitmap,
30213bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet                    NinePatch_Delegate.serialize(chunk),
30313bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet                    paddingRect, null);
30413bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet        }
30513bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet
30613bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet        return null;
30713bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet    }
30856a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet
3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // ------- TypedValue stuff
3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // This is taken from //device/libs/utils/ResourceTypes.cpp
31156a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet
3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final class UnitEntry {
3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String name;
3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int type;
3159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int unit;
3169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        float scale;
31756a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet
3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        UnitEntry(String name, int type, int unit, float scale) {
3199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.name = name;
3209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.type = type;
3219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.unit = unit;
3229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.scale = scale;
3239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private final static UnitEntry[] sUnitNames = new UnitEntry[] {
3279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        new UnitEntry("px", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_PX, 1.0f),
3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        new UnitEntry("dip", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_DIP, 1.0f),
3299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        new UnitEntry("dp", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_DIP, 1.0f),
3309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        new UnitEntry("sp", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_SP, 1.0f),
3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        new UnitEntry("pt", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_PT, 1.0f),
3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        new UnitEntry("in", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_IN, 1.0f),
3339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        new UnitEntry("mm", TypedValue.TYPE_DIMENSION, TypedValue.COMPLEX_UNIT_MM, 1.0f),
3349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        new UnitEntry("%", TypedValue.TYPE_FRACTION, TypedValue.COMPLEX_UNIT_FRACTION, 1.0f/100),
3359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        new UnitEntry("%p", TypedValue.TYPE_FRACTION, TypedValue.COMPLEX_UNIT_FRACTION_PARENT, 1.0f/100),
3369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
33756a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet
3389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
33904ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet     * Returns the raw value from the given attribute float-type value string.
3409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This object is only valid until the next call on to {@link ResourceHelper}.
3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
34204ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet    public static TypedValue getValue(String attribute, String value, boolean requireUnit) {
34304ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet        if (parseFloatAttribute(attribute, value, mValue, requireUnit)) {
3449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mValue;
3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
34656a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet
3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return null;
3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
34956a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet
3509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
35104ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet     * Parse a float attribute and return the parsed value into a given TypedValue.
35204ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet     * @param attribute the name of the attribute. Can be null if <var>requireUnit</var> is false.
35304ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet     * @param value the string value of the attribute
35404ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet     * @param outValue the TypedValue to receive the parsed value
35504ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet     * @param requireUnit whether the value is expected to contain a unit.
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return true if success.
3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
35804ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet    public static boolean parseFloatAttribute(String attribute, String value,
35904ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet            TypedValue outValue, boolean requireUnit) {
36004ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet        assert requireUnit == false || attribute != null;
36104ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet
3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // remove the space before and after
36304ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet        value = value.trim();
36404ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet        int len = value.length();
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (len <= 0) {
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // check that there's no non ascii characters.
37104ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet        char[] buf = value.toCharArray();
3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0 ; i < len ; i++) {
3739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (buf[i] > 255) {
3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return false;
3759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // check the first character
3796f2fb570642189ec2b6068632c25f02391007bb5Xavier Ducrohet        if (buf[0] < '0' && buf[0] > '9' && buf[0] != '.' && buf[0] != '-') {
3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
3819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
38256a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet
3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // now look for the string that is after the float...
38404ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet        Matcher m = sFloatPattern.matcher(value);
3859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (m.matches()) {
3869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String f_str = m.group(1);
3879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String end = m.group(2);
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            float f;
3909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                f = Float.parseFloat(f_str);
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (NumberFormatException e) {
3939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // this shouldn't happen with the regexp above.
3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return false;
3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
39656a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet
3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (end.length() > 0 && end.charAt(0) != ' ') {
3989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Might be a unit...
3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (parseUnit(end, outValue, sFloatOut)) {
40004ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet                    computeTypedValue(outValue, f, sFloatOut[0]);
4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return true;
4029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
4039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return false;
4049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
40556a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet
4069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // make sure it's only spaces at the end.
4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            end = end.trim();
40856a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet
4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (end.length() == 0) {
4109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (outValue != null) {
41104ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet                    if (requireUnit == false) {
41204ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet                        outValue.type = TypedValue.TYPE_FLOAT;
41304ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet                        outValue.data = Float.floatToIntBits(f);
41404ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet                    } else {
41504ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet                        // no unit when required? Use dp and out an error.
41604ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet                        applyUnit(sUnitNames[1], outValue, sFloatOut);
41704ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet                        computeTypedValue(outValue, f, sFloatOut[0]);
41804ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet
41904ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet                        Bridge.getLog().error(LayoutLog.TAG_RESOURCES_RESOLVE,
42004ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet                                String.format(
42104ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet                                        "Dimension \"%1$s\" in attribute \"%2$s\" is missing unit!",
42204ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet                                        value, attribute),
42304ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet                                null);
42404ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet                    }
4259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return true;
4269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return false;
4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
43256a92e3ba0d7da310b4c6e09f54dee10f1f395bbXavier Ducrohet
43304ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet    private static void computeTypedValue(TypedValue outValue, float value, float scale) {
43404ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet        value *= scale;
43504ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet        boolean neg = value < 0;
43604ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet        if (neg) {
43704ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet            value = -value;
43804ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet        }
43904ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet        long bits = (long)(value*(1<<23)+.5f);
44004ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet        int radix;
44104ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet        int shift;
44204ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet        if ((bits&0x7fffff) == 0) {
44304ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet            // Always use 23p0 if there is no fraction, just to make
44404ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet            // things easier to read.
44504ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet            radix = TypedValue.COMPLEX_RADIX_23p0;
44604ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet            shift = 23;
44704ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet        } else if ((bits&0xffffffffff800000L) == 0) {
44804ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet            // Magnitude is zero -- can fit in 0 bits of precision.
44904ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet            radix = TypedValue.COMPLEX_RADIX_0p23;
45004ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet            shift = 0;
45104ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet        } else if ((bits&0xffffffff80000000L) == 0) {
45204ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet            // Magnitude can fit in 8 bits of precision.
45304ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet            radix = TypedValue.COMPLEX_RADIX_8p15;
45404ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet            shift = 8;
45504ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet        } else if ((bits&0xffffff8000000000L) == 0) {
45604ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet            // Magnitude can fit in 16 bits of precision.
45704ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet            radix = TypedValue.COMPLEX_RADIX_16p7;
45804ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet            shift = 16;
45904ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet        } else {
46004ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet            // Magnitude needs entire range, so no fractional part.
46104ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet            radix = TypedValue.COMPLEX_RADIX_23p0;
46204ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet            shift = 23;
46304ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet        }
46404ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet        int mantissa = (int)(
46504ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet            (bits>>shift) & TypedValue.COMPLEX_MANTISSA_MASK);
46604ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet        if (neg) {
46704ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet            mantissa = (-mantissa) & TypedValue.COMPLEX_MANTISSA_MASK;
46804ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet        }
46904ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet        outValue.data |=
47004ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet            (radix<<TypedValue.COMPLEX_RADIX_SHIFT)
47104ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet            | (mantissa<<TypedValue.COMPLEX_MANTISSA_SHIFT);
47204ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet    }
47304ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet
4749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static boolean parseUnit(String str, TypedValue outValue, float[] outScale) {
4759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        str = str.trim();
4769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (UnitEntry unit : sUnitNames) {
4789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (unit.name.equals(str)) {
47904ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet                applyUnit(unit, outValue, outScale);
4809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return true;
4819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return false;
4859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
48604ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet
48704ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet    private static void applyUnit(UnitEntry unit, TypedValue outValue, float[] outScale) {
48804ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet        outValue.type = unit.type;
48904ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet        outValue.data = unit.unit << TypedValue.COMPLEX_UNIT_SHIFT;
49004ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet        outScale[0] = unit.scale;
49104ce81113107d2bfa0b8248b13145b4cf24cb943Xavier Ducrohet    }
4929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
49313bdc3355c781dc2614f2810a42d3a9e73f5bed9Xavier Ducrohet
494