Resources.java revision 5d911c3a95cf4db4cf1cc0c8d4d253553d50c550
1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.content.res;
18
19import com.android.internal.util.XmlUtils;
20
21import org.xmlpull.v1.XmlPullParser;
22import org.xmlpull.v1.XmlPullParserException;
23
24import android.content.pm.ActivityInfo;
25import android.graphics.Movie;
26import android.graphics.drawable.Drawable;
27import android.graphics.drawable.ColorDrawable;
28import android.graphics.drawable.Drawable.ConstantState;
29import android.os.Build;
30import android.os.Bundle;
31import android.util.AttributeSet;
32import android.util.DisplayMetrics;
33import android.util.Log;
34import android.util.Slog;
35import android.util.TypedValue;
36import android.util.LongSparseArray;
37
38import java.io.IOException;
39import java.io.InputStream;
40import java.lang.ref.WeakReference;
41import java.util.Locale;
42
43import libcore.icu.NativePluralRules;
44
45/**
46 * Class for accessing an application's resources.  This sits on top of the
47 * asset manager of the application (accessible through {@link #getAssets}) and
48 * provides a high-level API for getting typed data from the assets.
49 *
50 * <p>The Android resource system keeps track of all non-code assets associated with an
51 * application. You can use this class to access your application's resources. You can generally
52 * acquire the {@link android.content.res.Resources} instance associated with your application
53 * with {@link android.content.Context#getResources getResources()}.</p>
54 *
55 * <p>The Android SDK tools compile your application's resources into the application binary
56 * at build time.  To use a resource, you must install it correctly in the source tree (inside
57 * your project's {@code res/} directory) and build your application.  As part of the build
58 * process, the SDK tools generate symbols for each resource, which you can use in your application
59 * code to access the resources.</p>
60 *
61 * <p>Using application resources makes it easy to update various characteristics of your
62 * application without modifying code, and&mdash;by providing sets of alternative
63 * resources&mdash;enables you to optimize your application for a variety of device configurations
64 * (such as for different languages and screen sizes). This is an important aspect of developing
65 * Android applications that are compatible on different types of devices.</p>
66 *
67 * <p>For more information about using resources, see the documentation about <a
68 * href="{@docRoot}guide/topics/resources/index.html">Application Resources</a>.</p>
69 */
70public class Resources {
71    static final String TAG = "Resources";
72    private static final boolean DEBUG_LOAD = false;
73    private static final boolean DEBUG_CONFIG = false;
74    private static final boolean DEBUG_ATTRIBUTES_CACHE = false;
75    private static final boolean TRACE_FOR_PRELOAD = false;
76    private static final boolean TRACE_FOR_MISS_PRELOAD = false;
77
78    private static final int ID_OTHER = 0x01000004;
79
80    private static final Object mSync = new Object();
81    /*package*/ static Resources mSystem = null;
82
83    // Information about preloaded resources.  Note that they are not
84    // protected by a lock, because while preloading in zygote we are all
85    // single-threaded, and after that these are immutable.
86    private static final LongSparseArray<Drawable.ConstantState> sPreloadedDrawables
87            = new LongSparseArray<Drawable.ConstantState>();
88    private static final LongSparseArray<ColorStateList> sPreloadedColorStateLists
89            = new LongSparseArray<ColorStateList>();
90    private static final LongSparseArray<Drawable.ConstantState> sPreloadedColorDrawables
91            = new LongSparseArray<Drawable.ConstantState>();
92    private static boolean mPreloaded;
93
94    /*package*/ final TypedValue mTmpValue = new TypedValue();
95    /*package*/ final Configuration mTmpConfig = new Configuration();
96
97    // These are protected by the mTmpValue lock.
98    private final LongSparseArray<WeakReference<Drawable.ConstantState> > mDrawableCache
99            = new LongSparseArray<WeakReference<Drawable.ConstantState> >();
100    private final LongSparseArray<WeakReference<ColorStateList> > mColorStateListCache
101            = new LongSparseArray<WeakReference<ColorStateList> >();
102    private final LongSparseArray<WeakReference<Drawable.ConstantState> > mColorDrawableCache
103            = new LongSparseArray<WeakReference<Drawable.ConstantState> >();
104    private boolean mPreloading;
105
106    /*package*/ TypedArray mCachedStyledAttributes = null;
107    RuntimeException mLastRetrievedAttrs = null;
108
109    private int mLastCachedXmlBlockIndex = -1;
110    private final int[] mCachedXmlBlockIds = { 0, 0, 0, 0 };
111    private final XmlBlock[] mCachedXmlBlocks = new XmlBlock[4];
112
113    /*package*/ final AssetManager mAssets;
114    private final Configuration mConfiguration = new Configuration();
115    /*package*/ final DisplayMetrics mMetrics = new DisplayMetrics();
116    private NativePluralRules mPluralRule;
117
118    private CompatibilityInfo mCompatibilityInfo;
119
120    /** @hide */
121    public static int selectDefaultTheme(int curTheme, int targetSdkVersion) {
122        return selectSystemTheme(curTheme, targetSdkVersion,
123                com.android.internal.R.style.Theme,
124                com.android.internal.R.style.Theme_Holo,
125                com.android.internal.R.style.Theme_DeviceDefault);
126    }
127
128    /** @hide */
129    public static int selectSystemTheme(int curTheme, int targetSdkVersion,
130            int orig, int holo, int deviceDefault) {
131        if (curTheme != 0) {
132            return curTheme;
133        }
134        if (targetSdkVersion < Build.VERSION_CODES.HONEYCOMB) {
135            return orig;
136        }
137        if (targetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
138            return holo;
139        }
140        return deviceDefault;
141    }
142
143    /**
144     * This exception is thrown by the resource APIs when a requested resource
145     * can not be found.
146     */
147    public static class NotFoundException extends RuntimeException {
148        public NotFoundException() {
149        }
150
151        public NotFoundException(String name) {
152            super(name);
153        }
154    }
155
156    /**
157     * Create a new Resources object on top of an existing set of assets in an
158     * AssetManager.
159     *
160     * @param assets Previously created AssetManager.
161     * @param metrics Current display metrics to consider when
162     *                selecting/computing resource values.
163     * @param config Desired device configuration to consider when
164     *               selecting/computing resource values (optional).
165     */
166    public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) {
167        this(assets, metrics, config, null);
168    }
169
170    /**
171     * Creates a new Resources object with CompatibilityInfo.
172     *
173     * @param assets Previously created AssetManager.
174     * @param metrics Current display metrics to consider when
175     *                selecting/computing resource values.
176     * @param config Desired device configuration to consider when
177     *               selecting/computing resource values (optional).
178     * @param compInfo this resource's compatibility info. It will use the default compatibility
179     *  info when it's null.
180     * @hide
181     */
182    public Resources(AssetManager assets, DisplayMetrics metrics,
183            Configuration config, CompatibilityInfo compInfo) {
184        mAssets = assets;
185        mMetrics.setToDefaults();
186        mCompatibilityInfo = compInfo;
187        updateConfiguration(config, metrics);
188        assets.ensureStringBlocks();
189    }
190
191    /**
192     * Return a global shared Resources object that provides access to only
193     * system resources (no application resources), and is not configured for
194     * the current screen (can not use dimension units, does not change based
195     * on orientation, etc).
196     */
197    public static Resources getSystem() {
198        synchronized (mSync) {
199            Resources ret = mSystem;
200            if (ret == null) {
201                ret = new Resources();
202                mSystem = ret;
203            }
204
205            return ret;
206        }
207    }
208
209    /**
210     * Return the string value associated with a particular resource ID.  The
211     * returned object will be a String if this is a plain string; it will be
212     * some other type of CharSequence if it is styled.
213     * {@more}
214     *
215     * @param id The desired resource identifier, as generated by the aapt
216     *           tool. This integer encodes the package, type, and resource
217     *           entry. The value 0 is an invalid identifier.
218     *
219     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
220     *
221     * @return CharSequence The string data associated with the resource, plus
222     *         possibly styled text information.
223     */
224    public CharSequence getText(int id) throws NotFoundException {
225        CharSequence res = mAssets.getResourceText(id);
226        if (res != null) {
227            return res;
228        }
229        throw new NotFoundException("String resource ID #0x"
230                                    + Integer.toHexString(id));
231    }
232
233    /**
234     * Return the character sequence associated with a particular resource ID for a particular
235     * numerical quantity.
236     *
237     * <p>See <a href="{@docRoot}guide/topics/resources/string-resource.html#Plurals">String
238     * Resources</a> for more on quantity strings.
239     *
240     * @param id The desired resource identifier, as generated by the aapt
241     *           tool. This integer encodes the package, type, and resource
242     *           entry. The value 0 is an invalid identifier.
243     * @param quantity The number used to get the correct string for the current language's
244     *           plural rules.
245     *
246     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
247     *
248     * @return CharSequence The string data associated with the resource, plus
249     *         possibly styled text information.
250     */
251    public CharSequence getQuantityText(int id, int quantity) throws NotFoundException {
252        NativePluralRules rule = getPluralRule();
253        CharSequence res = mAssets.getResourceBagText(id,
254                attrForQuantityCode(rule.quantityForInt(quantity)));
255        if (res != null) {
256            return res;
257        }
258        res = mAssets.getResourceBagText(id, ID_OTHER);
259        if (res != null) {
260            return res;
261        }
262        throw new NotFoundException("Plural resource ID #0x" + Integer.toHexString(id)
263                + " quantity=" + quantity
264                + " item=" + stringForQuantityCode(rule.quantityForInt(quantity)));
265    }
266
267    private NativePluralRules getPluralRule() {
268        synchronized (mSync) {
269            if (mPluralRule == null) {
270                mPluralRule = NativePluralRules.forLocale(mConfiguration.locale);
271            }
272            return mPluralRule;
273        }
274    }
275
276    private static int attrForQuantityCode(int quantityCode) {
277        switch (quantityCode) {
278            case NativePluralRules.ZERO: return 0x01000005;
279            case NativePluralRules.ONE:  return 0x01000006;
280            case NativePluralRules.TWO:  return 0x01000007;
281            case NativePluralRules.FEW:  return 0x01000008;
282            case NativePluralRules.MANY: return 0x01000009;
283            default:                     return ID_OTHER;
284        }
285    }
286
287    private static String stringForQuantityCode(int quantityCode) {
288        switch (quantityCode) {
289            case NativePluralRules.ZERO: return "zero";
290            case NativePluralRules.ONE:  return "one";
291            case NativePluralRules.TWO:  return "two";
292            case NativePluralRules.FEW:  return "few";
293            case NativePluralRules.MANY: return "many";
294            default:                     return "other";
295        }
296    }
297
298    /**
299     * Return the string value associated with a particular resource ID.  It
300     * will be stripped of any styled text information.
301     * {@more}
302     *
303     * @param id The desired resource identifier, as generated by the aapt
304     *           tool. This integer encodes the package, type, and resource
305     *           entry. The value 0 is an invalid identifier.
306     *
307     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
308     *
309     * @return String The string data associated with the resource,
310     * stripped of styled text information.
311     */
312    public String getString(int id) throws NotFoundException {
313        CharSequence res = getText(id);
314        if (res != null) {
315            return res.toString();
316        }
317        throw new NotFoundException("String resource ID #0x"
318                                    + Integer.toHexString(id));
319    }
320
321
322    /**
323     * Return the string value associated with a particular resource ID,
324     * substituting the format arguments as defined in {@link java.util.Formatter}
325     * and {@link java.lang.String#format}. It will be stripped of any styled text
326     * information.
327     * {@more}
328     *
329     * @param id The desired resource identifier, as generated by the aapt
330     *           tool. This integer encodes the package, type, and resource
331     *           entry. The value 0 is an invalid identifier.
332     *
333     * @param formatArgs The format arguments that will be used for substitution.
334     *
335     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
336     *
337     * @return String The string data associated with the resource,
338     * stripped of styled text information.
339     */
340    public String getString(int id, Object... formatArgs) throws NotFoundException {
341        String raw = getString(id);
342        return String.format(mConfiguration.locale, raw, formatArgs);
343    }
344
345    /**
346     * Return the string value associated with a particular resource ID for a particular
347     * numerical quantity, substituting the format arguments as defined in
348     * {@link java.util.Formatter} and {@link java.lang.String#format}. It will be
349     * stripped of any styled text information.
350     * {@more}
351     *
352     * <p>See <a href="{@docRoot}guide/topics/resources/string-resource.html#Plurals">String
353     * Resources</a> for more on quantity strings.
354     *
355     * @param id The desired resource identifier, as generated by the aapt
356     *           tool. This integer encodes the package, type, and resource
357     *           entry. The value 0 is an invalid identifier.
358     * @param quantity The number used to get the correct string for the current language's
359     *           plural rules.
360     * @param formatArgs The format arguments that will be used for substitution.
361     *
362     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
363     *
364     * @return String The string data associated with the resource,
365     * stripped of styled text information.
366     */
367    public String getQuantityString(int id, int quantity, Object... formatArgs)
368            throws NotFoundException {
369        String raw = getQuantityText(id, quantity).toString();
370        return String.format(mConfiguration.locale, raw, formatArgs);
371    }
372
373    /**
374     * Return the string value associated with a particular resource ID for a particular
375     * numerical quantity.
376     *
377     * <p>See <a href="{@docRoot}guide/topics/resources/string-resource.html#Plurals">String
378     * Resources</a> for more on quantity strings.
379     *
380     * @param id The desired resource identifier, as generated by the aapt
381     *           tool. This integer encodes the package, type, and resource
382     *           entry. The value 0 is an invalid identifier.
383     * @param quantity The number used to get the correct string for the current language's
384     *           plural rules.
385     *
386     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
387     *
388     * @return String The string data associated with the resource,
389     * stripped of styled text information.
390     */
391    public String getQuantityString(int id, int quantity) throws NotFoundException {
392        return getQuantityText(id, quantity).toString();
393    }
394
395    /**
396     * Return the string value associated with a particular resource ID.  The
397     * returned object will be a String if this is a plain string; it will be
398     * some other type of CharSequence if it is styled.
399     *
400     * @param id The desired resource identifier, as generated by the aapt
401     *           tool. This integer encodes the package, type, and resource
402     *           entry. The value 0 is an invalid identifier.
403     *
404     * @param def The default CharSequence to return.
405     *
406     * @return CharSequence The string data associated with the resource, plus
407     *         possibly styled text information, or def if id is 0 or not found.
408     */
409    public CharSequence getText(int id, CharSequence def) {
410        CharSequence res = id != 0 ? mAssets.getResourceText(id) : null;
411        return res != null ? res : def;
412    }
413
414    /**
415     * Return the styled text array associated with a particular resource ID.
416     *
417     * @param id The desired resource identifier, as generated by the aapt
418     *           tool. This integer encodes the package, type, and resource
419     *           entry. The value 0 is an invalid identifier.
420     *
421     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
422     *
423     * @return The styled text array associated with the resource.
424     */
425    public CharSequence[] getTextArray(int id) throws NotFoundException {
426        CharSequence[] res = mAssets.getResourceTextArray(id);
427        if (res != null) {
428            return res;
429        }
430        throw new NotFoundException("Text array resource ID #0x"
431                                    + Integer.toHexString(id));
432    }
433
434    /**
435     * Return the string array associated with a particular resource ID.
436     *
437     * @param id The desired resource identifier, as generated by the aapt
438     *           tool. This integer encodes the package, type, and resource
439     *           entry. The value 0 is an invalid identifier.
440     *
441     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
442     *
443     * @return The string array associated with the resource.
444     */
445    public String[] getStringArray(int id) throws NotFoundException {
446        String[] res = mAssets.getResourceStringArray(id);
447        if (res != null) {
448            return res;
449        }
450        throw new NotFoundException("String array resource ID #0x"
451                                    + Integer.toHexString(id));
452    }
453
454    /**
455     * Return the int array associated with a particular resource ID.
456     *
457     * @param id The desired resource identifier, as generated by the aapt
458     *           tool. This integer encodes the package, type, and resource
459     *           entry. The value 0 is an invalid identifier.
460     *
461     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
462     *
463     * @return The int array associated with the resource.
464     */
465    public int[] getIntArray(int id) throws NotFoundException {
466        int[] res = mAssets.getArrayIntResource(id);
467        if (res != null) {
468            return res;
469        }
470        throw new NotFoundException("Int array resource ID #0x"
471                                    + Integer.toHexString(id));
472    }
473
474    /**
475     * Return an array of heterogeneous values.
476     *
477     * @param id The desired resource identifier, as generated by the aapt
478     *           tool. This integer encodes the package, type, and resource
479     *           entry. The value 0 is an invalid identifier.
480     *
481     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
482     *
483     * @return Returns a TypedArray holding an array of the array values.
484     * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
485     * when done with it.
486     */
487    public TypedArray obtainTypedArray(int id) throws NotFoundException {
488        int len = mAssets.getArraySize(id);
489        if (len < 0) {
490            throw new NotFoundException("Array resource ID #0x"
491                                        + Integer.toHexString(id));
492        }
493
494        TypedArray array = getCachedStyledAttributes(len);
495        array.mLength = mAssets.retrieveArray(id, array.mData);
496        array.mIndices[0] = 0;
497
498        return array;
499    }
500
501    /**
502     * Retrieve a dimensional for a particular resource ID.  Unit
503     * conversions are based on the current {@link DisplayMetrics} associated
504     * with the resources.
505     *
506     * @param id The desired resource identifier, as generated by the aapt
507     *           tool. This integer encodes the package, type, and resource
508     *           entry. The value 0 is an invalid identifier.
509     *
510     * @return Resource dimension value multiplied by the appropriate
511     * metric.
512     *
513     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
514     *
515     * @see #getDimensionPixelOffset
516     * @see #getDimensionPixelSize
517     */
518    public float getDimension(int id) throws NotFoundException {
519        synchronized (mTmpValue) {
520            TypedValue value = mTmpValue;
521            getValue(id, value, true);
522            if (value.type == TypedValue.TYPE_DIMENSION) {
523                return TypedValue.complexToDimension(value.data, mMetrics);
524            }
525            throw new NotFoundException(
526                    "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
527                    + Integer.toHexString(value.type) + " is not valid");
528        }
529    }
530
531    /**
532     * Retrieve a dimensional for a particular resource ID for use
533     * as an offset in raw pixels.  This is the same as
534     * {@link #getDimension}, except the returned value is converted to
535     * integer pixels for you.  An offset conversion involves simply
536     * truncating the base value to an integer.
537     *
538     * @param id The desired resource identifier, as generated by the aapt
539     *           tool. This integer encodes the package, type, and resource
540     *           entry. The value 0 is an invalid identifier.
541     *
542     * @return Resource dimension value multiplied by the appropriate
543     * metric and truncated to integer pixels.
544     *
545     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
546     *
547     * @see #getDimension
548     * @see #getDimensionPixelSize
549     */
550    public int getDimensionPixelOffset(int id) throws NotFoundException {
551        synchronized (mTmpValue) {
552            TypedValue value = mTmpValue;
553            getValue(id, value, true);
554            if (value.type == TypedValue.TYPE_DIMENSION) {
555                return TypedValue.complexToDimensionPixelOffset(
556                        value.data, mMetrics);
557            }
558            throw new NotFoundException(
559                    "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
560                    + Integer.toHexString(value.type) + " is not valid");
561        }
562    }
563
564    /**
565     * Retrieve a dimensional for a particular resource ID for use
566     * as a size in raw pixels.  This is the same as
567     * {@link #getDimension}, except the returned value is converted to
568     * integer pixels for use as a size.  A size conversion involves
569     * rounding the base value, and ensuring that a non-zero base value
570     * is at least one pixel in size.
571     *
572     * @param id The desired resource identifier, as generated by the aapt
573     *           tool. This integer encodes the package, type, and resource
574     *           entry. The value 0 is an invalid identifier.
575     *
576     * @return Resource dimension value multiplied by the appropriate
577     * metric and truncated to integer pixels.
578     *
579     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
580     *
581     * @see #getDimension
582     * @see #getDimensionPixelOffset
583     */
584    public int getDimensionPixelSize(int id) throws NotFoundException {
585        synchronized (mTmpValue) {
586            TypedValue value = mTmpValue;
587            getValue(id, value, true);
588            if (value.type == TypedValue.TYPE_DIMENSION) {
589                return TypedValue.complexToDimensionPixelSize(
590                        value.data, mMetrics);
591            }
592            throw new NotFoundException(
593                    "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
594                    + Integer.toHexString(value.type) + " is not valid");
595        }
596    }
597
598    /**
599     * Retrieve a fractional unit for a particular resource ID.
600     *
601     * @param id The desired resource identifier, as generated by the aapt
602     *           tool. This integer encodes the package, type, and resource
603     *           entry. The value 0 is an invalid identifier.
604     * @param base The base value of this fraction.  In other words, a
605     *             standard fraction is multiplied by this value.
606     * @param pbase The parent base value of this fraction.  In other
607     *             words, a parent fraction (nn%p) is multiplied by this
608     *             value.
609     *
610     * @return Attribute fractional value multiplied by the appropriate
611     * base value.
612     *
613     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
614     */
615    public float getFraction(int id, int base, int pbase) {
616        synchronized (mTmpValue) {
617            TypedValue value = mTmpValue;
618            getValue(id, value, true);
619            if (value.type == TypedValue.TYPE_FRACTION) {
620                return TypedValue.complexToFraction(value.data, base, pbase);
621            }
622            throw new NotFoundException(
623                    "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
624                    + Integer.toHexString(value.type) + " is not valid");
625        }
626    }
627
628    /**
629     * Return a drawable object associated with a particular resource ID.
630     * Various types of objects will be returned depending on the underlying
631     * resource -- for example, a solid color, PNG image, scalable image, etc.
632     * The Drawable API hides these implementation details.
633     *
634     * @param id The desired resource identifier, as generated by the aapt
635     *           tool. This integer encodes the package, type, and resource
636     *           entry. The value 0 is an invalid identifier.
637     *
638     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
639     *
640     * @return Drawable An object that can be used to draw this resource.
641     */
642    public Drawable getDrawable(int id) throws NotFoundException {
643        synchronized (mTmpValue) {
644            TypedValue value = mTmpValue;
645            getValue(id, value, true);
646            return loadDrawable(value, id);
647        }
648    }
649
650    /**
651     * Return a drawable object associated with a particular resource ID for the
652     * given screen density in DPI. This will set the drawable's density to be
653     * the device's density multiplied by the ratio of actual drawable density
654     * to requested density. This allows the drawable to be scaled up to the
655     * correct size if needed. Various types of objects will be returned
656     * depending on the underlying resource -- for example, a solid color, PNG
657     * image, scalable image, etc. The Drawable API hides these implementation
658     * details.
659     *
660     * @param id The desired resource identifier, as generated by the aapt tool.
661     *            This integer encodes the package, type, and resource entry.
662     *            The value 0 is an invalid identifier.
663     * @param density the desired screen density indicated by the resource as
664     *            found in {@link DisplayMetrics}.
665     * @throws NotFoundException Throws NotFoundException if the given ID does
666     *             not exist.
667     * @return Drawable An object that can be used to draw this resource.
668     */
669    public Drawable getDrawableForDensity(int id, int density) throws NotFoundException {
670        synchronized (mTmpValue) {
671            TypedValue value = mTmpValue;
672            getValueForDensity(id, density, value, true);
673
674            /*
675             * Pretend the requested density is actually the display density. If
676             * the drawable returned is not the requested density, then force it
677             * to be scaled later by dividing its density by the ratio of
678             * requested density to actual device density. Drawables that have
679             * undefined density or no density don't need to be handled here.
680             */
681            if (value.density > 0 && value.density != TypedValue.DENSITY_NONE) {
682                if (value.density == density) {
683                    value.density = DisplayMetrics.DENSITY_DEVICE;
684                } else {
685                    value.density = (value.density * DisplayMetrics.DENSITY_DEVICE) / density;
686                }
687            }
688
689            return loadDrawable(value, id);
690        }
691    }
692
693    /**
694     * Return a movie object associated with the particular resource ID.
695     * @param id The desired resource identifier, as generated by the aapt
696     *           tool. This integer encodes the package, type, and resource
697     *           entry. The value 0 is an invalid identifier.
698     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
699     *
700     */
701    public Movie getMovie(int id) throws NotFoundException {
702        InputStream is = openRawResource(id);
703        Movie movie = Movie.decodeStream(is);
704        try {
705            is.close();
706        }
707        catch (java.io.IOException e) {
708            // don't care, since the return value is valid
709        }
710        return movie;
711    }
712
713    /**
714     * Return a color integer associated with a particular resource ID.
715     * If the resource holds a complex
716     * {@link android.content.res.ColorStateList}, then the default color from
717     * the set is returned.
718     *
719     * @param id The desired resource identifier, as generated by the aapt
720     *           tool. This integer encodes the package, type, and resource
721     *           entry. The value 0 is an invalid identifier.
722     *
723     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
724     *
725     * @return Returns a single color value in the form 0xAARRGGBB.
726     */
727    public int getColor(int id) throws NotFoundException {
728        synchronized (mTmpValue) {
729            TypedValue value = mTmpValue;
730            getValue(id, value, true);
731            if (value.type >= TypedValue.TYPE_FIRST_INT
732                && value.type <= TypedValue.TYPE_LAST_INT) {
733                return value.data;
734            } else if (value.type == TypedValue.TYPE_STRING) {
735                ColorStateList csl = loadColorStateList(mTmpValue, id);
736                return csl.getDefaultColor();
737            }
738            throw new NotFoundException(
739                "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
740                + Integer.toHexString(value.type) + " is not valid");
741        }
742    }
743
744    /**
745     * Return a color state list associated with a particular resource ID.  The
746     * resource may contain either a single raw color value, or a complex
747     * {@link android.content.res.ColorStateList} holding multiple possible colors.
748     *
749     * @param id The desired resource identifier of a {@link ColorStateList},
750     *        as generated by the aapt tool. This integer encodes the package, type, and resource
751     *        entry. The value 0 is an invalid identifier.
752     *
753     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
754     *
755     * @return Returns a ColorStateList object containing either a single
756     * solid color or multiple colors that can be selected based on a state.
757     */
758    public ColorStateList getColorStateList(int id) throws NotFoundException {
759        synchronized (mTmpValue) {
760            TypedValue value = mTmpValue;
761            getValue(id, value, true);
762            return loadColorStateList(value, id);
763        }
764    }
765
766    /**
767     * Return a boolean associated with a particular resource ID.  This can be
768     * used with any integral resource value, and will return true if it is
769     * non-zero.
770     *
771     * @param id The desired resource identifier, as generated by the aapt
772     *           tool. This integer encodes the package, type, and resource
773     *           entry. The value 0 is an invalid identifier.
774     *
775     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
776     *
777     * @return Returns the boolean value contained in the resource.
778     */
779    public boolean getBoolean(int id) throws NotFoundException {
780        synchronized (mTmpValue) {
781            TypedValue value = mTmpValue;
782            getValue(id, value, true);
783            if (value.type >= TypedValue.TYPE_FIRST_INT
784                && value.type <= TypedValue.TYPE_LAST_INT) {
785                return value.data != 0;
786            }
787            throw new NotFoundException(
788                "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
789                + Integer.toHexString(value.type) + " is not valid");
790        }
791    }
792
793    /**
794     * Return an integer associated with a particular resource ID.
795     *
796     * @param id The desired resource identifier, as generated by the aapt
797     *           tool. This integer encodes the package, type, and resource
798     *           entry. The value 0 is an invalid identifier.
799     *
800     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
801     *
802     * @return Returns the integer value contained in the resource.
803     */
804    public int getInteger(int id) throws NotFoundException {
805        synchronized (mTmpValue) {
806            TypedValue value = mTmpValue;
807            getValue(id, value, true);
808            if (value.type >= TypedValue.TYPE_FIRST_INT
809                && value.type <= TypedValue.TYPE_LAST_INT) {
810                return value.data;
811            }
812            throw new NotFoundException(
813                "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
814                + Integer.toHexString(value.type) + " is not valid");
815        }
816    }
817
818    /**
819     * Return an XmlResourceParser through which you can read a view layout
820     * description for the given resource ID.  This parser has limited
821     * functionality -- in particular, you can't change its input, and only
822     * the high-level events are available.
823     *
824     * <p>This function is really a simple wrapper for calling
825     * {@link #getXml} with a layout resource.
826     *
827     * @param id The desired resource identifier, as generated by the aapt
828     *           tool. This integer encodes the package, type, and resource
829     *           entry. The value 0 is an invalid identifier.
830     *
831     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
832     *
833     * @return A new parser object through which you can read
834     *         the XML data.
835     *
836     * @see #getXml
837     */
838    public XmlResourceParser getLayout(int id) throws NotFoundException {
839        return loadXmlResourceParser(id, "layout");
840    }
841
842    /**
843     * Return an XmlResourceParser through which you can read an animation
844     * description for the given resource ID.  This parser has limited
845     * functionality -- in particular, you can't change its input, and only
846     * the high-level events are available.
847     *
848     * <p>This function is really a simple wrapper for calling
849     * {@link #getXml} with an animation resource.
850     *
851     * @param id The desired resource identifier, as generated by the aapt
852     *           tool. This integer encodes the package, type, and resource
853     *           entry. The value 0 is an invalid identifier.
854     *
855     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
856     *
857     * @return A new parser object through which you can read
858     *         the XML data.
859     *
860     * @see #getXml
861     */
862    public XmlResourceParser getAnimation(int id) throws NotFoundException {
863        return loadXmlResourceParser(id, "anim");
864    }
865
866    /**
867     * Return an XmlResourceParser through which you can read a generic XML
868     * resource for the given resource ID.
869     *
870     * <p>The XmlPullParser implementation returned here has some limited
871     * functionality.  In particular, you can't change its input, and only
872     * high-level parsing events are available (since the document was
873     * pre-parsed for you at build time, which involved merging text and
874     * stripping comments).
875     *
876     * @param id The desired resource identifier, as generated by the aapt
877     *           tool. This integer encodes the package, type, and resource
878     *           entry. The value 0 is an invalid identifier.
879     *
880     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
881     *
882     * @return A new parser object through which you can read
883     *         the XML data.
884     *
885     * @see android.util.AttributeSet
886     */
887    public XmlResourceParser getXml(int id) throws NotFoundException {
888        return loadXmlResourceParser(id, "xml");
889    }
890
891    /**
892     * Open a data stream for reading a raw resource.  This can only be used
893     * with resources whose value is the name of an asset files -- that is, it can be
894     * used to open drawable, sound, and raw resources; it will fail on string
895     * and color resources.
896     *
897     * @param id The resource identifier to open, as generated by the appt
898     *           tool.
899     *
900     * @return InputStream Access to the resource data.
901     *
902     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
903     *
904     */
905    public InputStream openRawResource(int id) throws NotFoundException {
906        synchronized (mTmpValue) {
907            return openRawResource(id, mTmpValue);
908        }
909    }
910
911    /**
912     * Open a data stream for reading a raw resource.  This can only be used
913     * with resources whose value is the name of an asset file -- that is, it can be
914     * used to open drawable, sound, and raw resources; it will fail on string
915     * and color resources.
916     *
917     * @param id The resource identifier to open, as generated by the appt tool.
918     * @param value The TypedValue object to hold the resource information.
919     *
920     * @return InputStream Access to the resource data.
921     *
922     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
923     */
924    public InputStream openRawResource(int id, TypedValue value) throws NotFoundException {
925        getValue(id, value, true);
926
927        try {
928            return mAssets.openNonAsset(value.assetCookie, value.string.toString(),
929                    AssetManager.ACCESS_STREAMING);
930        } catch (Exception e) {
931            NotFoundException rnf = new NotFoundException("File " + value.string.toString() +
932                    " from drawable resource ID #0x" + Integer.toHexString(id));
933            rnf.initCause(e);
934            throw rnf;
935        }
936    }
937
938    /**
939     * Open a file descriptor for reading a raw resource.  This can only be used
940     * with resources whose value is the name of an asset files -- that is, it can be
941     * used to open drawable, sound, and raw resources; it will fail on string
942     * and color resources.
943     *
944     * <p>This function only works for resources that are stored in the package
945     * as uncompressed data, which typically includes things like mp3 files
946     * and png images.
947     *
948     * @param id The resource identifier to open, as generated by the appt
949     *           tool.
950     *
951     * @return AssetFileDescriptor A new file descriptor you can use to read
952     * the resource.  This includes the file descriptor itself, as well as the
953     * offset and length of data where the resource appears in the file.  A
954     * null is returned if the file exists but is compressed.
955     *
956     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
957     *
958     */
959    public AssetFileDescriptor openRawResourceFd(int id) throws NotFoundException {
960        synchronized (mTmpValue) {
961            TypedValue value = mTmpValue;
962            getValue(id, value, true);
963
964            try {
965                return mAssets.openNonAssetFd(
966                    value.assetCookie, value.string.toString());
967            } catch (Exception e) {
968                NotFoundException rnf = new NotFoundException(
969                    "File " + value.string.toString()
970                    + " from drawable resource ID #0x"
971                    + Integer.toHexString(id));
972                rnf.initCause(e);
973                throw rnf;
974            }
975
976        }
977    }
978
979    /**
980     * Return the raw data associated with a particular resource ID.
981     *
982     * @param id The desired resource identifier, as generated by the aapt
983     *           tool. This integer encodes the package, type, and resource
984     *           entry. The value 0 is an invalid identifier.
985     * @param outValue Object in which to place the resource data.
986     * @param resolveRefs If true, a resource that is a reference to another
987     *                    resource will be followed so that you receive the
988     *                    actual final resource data.  If false, the TypedValue
989     *                    will be filled in with the reference itself.
990     *
991     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
992     *
993     */
994    public void getValue(int id, TypedValue outValue, boolean resolveRefs)
995            throws NotFoundException {
996        boolean found = mAssets.getResourceValue(id, 0, outValue, resolveRefs);
997        if (found) {
998            return;
999        }
1000        throw new NotFoundException("Resource ID #0x"
1001                                    + Integer.toHexString(id));
1002    }
1003
1004    /**
1005     * Get the raw value associated with a resource with associated density.
1006     *
1007     * @param id resource identifier
1008     * @param density density in DPI
1009     * @param resolveRefs If true, a resource that is a reference to another
1010     *            resource will be followed so that you receive the actual final
1011     *            resource data. If false, the TypedValue will be filled in with
1012     *            the reference itself.
1013     * @throws NotFoundException Throws NotFoundException if the given ID does
1014     *             not exist.
1015     * @see #getValue(String, TypedValue, boolean)
1016     */
1017    public void getValueForDensity(int id, int density, TypedValue outValue, boolean resolveRefs)
1018            throws NotFoundException {
1019        boolean found = mAssets.getResourceValue(id, density, outValue, resolveRefs);
1020        if (found) {
1021            return;
1022        }
1023        throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id));
1024    }
1025
1026    /**
1027     * Return the raw data associated with a particular resource ID.
1028     * See getIdentifier() for information on how names are mapped to resource
1029     * IDs, and getString(int) for information on how string resources are
1030     * retrieved.
1031     *
1032     * <p>Note: use of this function is discouraged.  It is much more
1033     * efficient to retrieve resources by identifier than by name.
1034     *
1035     * @param name The name of the desired resource.  This is passed to
1036     *             getIdentifier() with a default type of "string".
1037     * @param outValue Object in which to place the resource data.
1038     * @param resolveRefs If true, a resource that is a reference to another
1039     *                    resource will be followed so that you receive the
1040     *                    actual final resource data.  If false, the TypedValue
1041     *                    will be filled in with the reference itself.
1042     *
1043     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1044     *
1045     */
1046    public void getValue(String name, TypedValue outValue, boolean resolveRefs)
1047            throws NotFoundException {
1048        int id = getIdentifier(name, "string", null);
1049        if (id != 0) {
1050            getValue(id, outValue, resolveRefs);
1051            return;
1052        }
1053        throw new NotFoundException("String resource name " + name);
1054    }
1055
1056    /**
1057     * This class holds the current attribute values for a particular theme.
1058     * In other words, a Theme is a set of values for resource attributes;
1059     * these are used in conjunction with {@link TypedArray}
1060     * to resolve the final value for an attribute.
1061     *
1062     * <p>The Theme's attributes come into play in two ways: (1) a styled
1063     * attribute can explicit reference a value in the theme through the
1064     * "?themeAttribute" syntax; (2) if no value has been defined for a
1065     * particular styled attribute, as a last resort we will try to find that
1066     * attribute's value in the Theme.
1067     *
1068     * <p>You will normally use the {@link #obtainStyledAttributes} APIs to
1069     * retrieve XML attributes with style and theme information applied.
1070     */
1071    public final class Theme {
1072        /**
1073         * Place new attribute values into the theme.  The style resource
1074         * specified by <var>resid</var> will be retrieved from this Theme's
1075         * resources, its values placed into the Theme object.
1076         *
1077         * <p>The semantics of this function depends on the <var>force</var>
1078         * argument:  If false, only values that are not already defined in
1079         * the theme will be copied from the system resource; otherwise, if
1080         * any of the style's attributes are already defined in the theme, the
1081         * current values in the theme will be overwritten.
1082         *
1083         * @param resid The resource ID of a style resource from which to
1084         *              obtain attribute values.
1085         * @param force If true, values in the style resource will always be
1086         *              used in the theme; otherwise, they will only be used
1087         *              if not already defined in the theme.
1088         */
1089        public void applyStyle(int resid, boolean force) {
1090            AssetManager.applyThemeStyle(mTheme, resid, force);
1091        }
1092
1093        /**
1094         * Set this theme to hold the same contents as the theme
1095         * <var>other</var>.  If both of these themes are from the same
1096         * Resources object, they will be identical after this function
1097         * returns.  If they are from different Resources, only the resources
1098         * they have in common will be set in this theme.
1099         *
1100         * @param other The existing Theme to copy from.
1101         */
1102        public void setTo(Theme other) {
1103            AssetManager.copyTheme(mTheme, other.mTheme);
1104        }
1105
1106        /**
1107         * Return a StyledAttributes holding the values defined by
1108         * <var>Theme</var> which are listed in <var>attrs</var>.
1109         *
1110         * <p>Be sure to call StyledAttributes.recycle() when you are done with
1111         * the array.
1112         *
1113         * @param attrs The desired attributes.
1114         *
1115         * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1116         *
1117         * @return Returns a TypedArray holding an array of the attribute values.
1118         * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
1119         * when done with it.
1120         *
1121         * @see Resources#obtainAttributes
1122         * @see #obtainStyledAttributes(int, int[])
1123         * @see #obtainStyledAttributes(AttributeSet, int[], int, int)
1124         */
1125        public TypedArray obtainStyledAttributes(int[] attrs) {
1126            int len = attrs.length;
1127            TypedArray array = getCachedStyledAttributes(len);
1128            array.mRsrcs = attrs;
1129            AssetManager.applyStyle(mTheme, 0, 0, 0, attrs,
1130                    array.mData, array.mIndices);
1131            return array;
1132        }
1133
1134        /**
1135         * Return a StyledAttributes holding the values defined by the style
1136         * resource <var>resid</var> which are listed in <var>attrs</var>.
1137         *
1138         * <p>Be sure to call StyledAttributes.recycle() when you are done with
1139         * the array.
1140         *
1141         * @param resid The desired style resource.
1142         * @param attrs The desired attributes in the style.
1143         *
1144         * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1145         *
1146         * @return Returns a TypedArray holding an array of the attribute values.
1147         * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
1148         * when done with it.
1149         *
1150         * @see Resources#obtainAttributes
1151         * @see #obtainStyledAttributes(int[])
1152         * @see #obtainStyledAttributes(AttributeSet, int[], int, int)
1153         */
1154        public TypedArray obtainStyledAttributes(int resid, int[] attrs)
1155                throws NotFoundException {
1156            int len = attrs.length;
1157            TypedArray array = getCachedStyledAttributes(len);
1158            array.mRsrcs = attrs;
1159
1160            AssetManager.applyStyle(mTheme, 0, resid, 0, attrs,
1161                    array.mData, array.mIndices);
1162            if (false) {
1163                int[] data = array.mData;
1164
1165                System.out.println("**********************************************************");
1166                System.out.println("**********************************************************");
1167                System.out.println("**********************************************************");
1168                System.out.println("Attributes:");
1169                String s = "  Attrs:";
1170                int i;
1171                for (i=0; i<attrs.length; i++) {
1172                    s = s + " 0x" + Integer.toHexString(attrs[i]);
1173                }
1174                System.out.println(s);
1175                s = "  Found:";
1176                TypedValue value = new TypedValue();
1177                for (i=0; i<attrs.length; i++) {
1178                    int d = i*AssetManager.STYLE_NUM_ENTRIES;
1179                    value.type = data[d+AssetManager.STYLE_TYPE];
1180                    value.data = data[d+AssetManager.STYLE_DATA];
1181                    value.assetCookie = data[d+AssetManager.STYLE_ASSET_COOKIE];
1182                    value.resourceId = data[d+AssetManager.STYLE_RESOURCE_ID];
1183                    s = s + " 0x" + Integer.toHexString(attrs[i])
1184                        + "=" + value;
1185                }
1186                System.out.println(s);
1187            }
1188            return array;
1189        }
1190
1191        /**
1192         * Return a StyledAttributes holding the attribute values in
1193         * <var>set</var>
1194         * that are listed in <var>attrs</var>.  In addition, if the given
1195         * AttributeSet specifies a style class (through the "style" attribute),
1196         * that style will be applied on top of the base attributes it defines.
1197         *
1198         * <p>Be sure to call StyledAttributes.recycle() when you are done with
1199         * the array.
1200         *
1201         * <p>When determining the final value of a particular attribute, there
1202         * are four inputs that come into play:</p>
1203         *
1204         * <ol>
1205         *     <li> Any attribute values in the given AttributeSet.
1206         *     <li> The style resource specified in the AttributeSet (named
1207         *     "style").
1208         *     <li> The default style specified by <var>defStyleAttr</var> and
1209         *     <var>defStyleRes</var>
1210         *     <li> The base values in this theme.
1211         * </ol>
1212         *
1213         * <p>Each of these inputs is considered in-order, with the first listed
1214         * taking precedence over the following ones.  In other words, if in the
1215         * AttributeSet you have supplied <code>&lt;Button
1216         * textColor="#ff000000"&gt;</code>, then the button's text will
1217         * <em>always</em> be black, regardless of what is specified in any of
1218         * the styles.
1219         *
1220         * @param set The base set of attribute values.  May be null.
1221         * @param attrs The desired attributes to be retrieved.
1222         * @param defStyleAttr An attribute in the current theme that contains a
1223         *                     reference to a style resource that supplies
1224         *                     defaults values for the StyledAttributes.  Can be
1225         *                     0 to not look for defaults.
1226         * @param defStyleRes A resource identifier of a style resource that
1227         *                    supplies default values for the StyledAttributes,
1228         *                    used only if defStyleAttr is 0 or can not be found
1229         *                    in the theme.  Can be 0 to not look for defaults.
1230         *
1231         * @return Returns a TypedArray holding an array of the attribute values.
1232         * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
1233         * when done with it.
1234         *
1235         * @see Resources#obtainAttributes
1236         * @see #obtainStyledAttributes(int[])
1237         * @see #obtainStyledAttributes(int, int[])
1238         */
1239        public TypedArray obtainStyledAttributes(AttributeSet set,
1240                int[] attrs, int defStyleAttr, int defStyleRes) {
1241            int len = attrs.length;
1242            TypedArray array = getCachedStyledAttributes(len);
1243
1244            // XXX note that for now we only work with compiled XML files.
1245            // To support generic XML files we will need to manually parse
1246            // out the attributes from the XML file (applying type information
1247            // contained in the resources and such).
1248            XmlBlock.Parser parser = (XmlBlock.Parser)set;
1249            AssetManager.applyStyle(
1250                mTheme, defStyleAttr, defStyleRes,
1251                parser != null ? parser.mParseState : 0, attrs,
1252                        array.mData, array.mIndices);
1253
1254            array.mRsrcs = attrs;
1255            array.mXml = parser;
1256
1257            if (false) {
1258                int[] data = array.mData;
1259
1260                System.out.println("Attributes:");
1261                String s = "  Attrs:";
1262                int i;
1263                for (i=0; i<set.getAttributeCount(); i++) {
1264                    s = s + " " + set.getAttributeName(i);
1265                    int id = set.getAttributeNameResource(i);
1266                    if (id != 0) {
1267                        s = s + "(0x" + Integer.toHexString(id) + ")";
1268                    }
1269                    s = s + "=" + set.getAttributeValue(i);
1270                }
1271                System.out.println(s);
1272                s = "  Found:";
1273                TypedValue value = new TypedValue();
1274                for (i=0; i<attrs.length; i++) {
1275                    int d = i*AssetManager.STYLE_NUM_ENTRIES;
1276                    value.type = data[d+AssetManager.STYLE_TYPE];
1277                    value.data = data[d+AssetManager.STYLE_DATA];
1278                    value.assetCookie = data[d+AssetManager.STYLE_ASSET_COOKIE];
1279                    value.resourceId = data[d+AssetManager.STYLE_RESOURCE_ID];
1280                    s = s + " 0x" + Integer.toHexString(attrs[i])
1281                        + "=" + value;
1282                }
1283                System.out.println(s);
1284            }
1285
1286            return array;
1287        }
1288
1289        /**
1290         * Retrieve the value of an attribute in the Theme.  The contents of
1291         * <var>outValue</var> are ultimately filled in by
1292         * {@link Resources#getValue}.
1293         *
1294         * @param resid The resource identifier of the desired theme
1295         *              attribute.
1296         * @param outValue Filled in with the ultimate resource value supplied
1297         *                 by the attribute.
1298         * @param resolveRefs If true, resource references will be walked; if
1299         *                    false, <var>outValue</var> may be a
1300         *                    TYPE_REFERENCE.  In either case, it will never
1301         *                    be a TYPE_ATTRIBUTE.
1302         *
1303         * @return boolean Returns true if the attribute was found and
1304         *         <var>outValue</var> is valid, else false.
1305         */
1306        public boolean resolveAttribute(int resid, TypedValue outValue,
1307                boolean resolveRefs) {
1308            boolean got = mAssets.getThemeValue(mTheme, resid, outValue, resolveRefs);
1309            if (false) {
1310                System.out.println(
1311                    "resolveAttribute #" + Integer.toHexString(resid)
1312                    + " got=" + got + ", type=0x" + Integer.toHexString(outValue.type)
1313                    + ", data=0x" + Integer.toHexString(outValue.data));
1314            }
1315            return got;
1316        }
1317
1318        /**
1319         * Print contents of this theme out to the log.  For debugging only.
1320         *
1321         * @param priority The log priority to use.
1322         * @param tag The log tag to use.
1323         * @param prefix Text to prefix each line printed.
1324         */
1325        public void dump(int priority, String tag, String prefix) {
1326            AssetManager.dumpTheme(mTheme, priority, tag, prefix);
1327        }
1328
1329        protected void finalize() throws Throwable {
1330            super.finalize();
1331            mAssets.releaseTheme(mTheme);
1332        }
1333
1334        /*package*/ Theme() {
1335            mAssets = Resources.this.mAssets;
1336            mTheme = mAssets.createTheme();
1337        }
1338
1339        private final AssetManager mAssets;
1340        private final int mTheme;
1341    }
1342
1343    /**
1344     * Generate a new Theme object for this set of Resources.  It initially
1345     * starts out empty.
1346     *
1347     * @return Theme The newly created Theme container.
1348     */
1349    public final Theme newTheme() {
1350        return new Theme();
1351    }
1352
1353    /**
1354     * Retrieve a set of basic attribute values from an AttributeSet, not
1355     * performing styling of them using a theme and/or style resources.
1356     *
1357     * @param set The current attribute values to retrieve.
1358     * @param attrs The specific attributes to be retrieved.
1359     * @return Returns a TypedArray holding an array of the attribute values.
1360     * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
1361     * when done with it.
1362     *
1363     * @see Theme#obtainStyledAttributes(AttributeSet, int[], int, int)
1364     */
1365    public TypedArray obtainAttributes(AttributeSet set, int[] attrs) {
1366        int len = attrs.length;
1367        TypedArray array = getCachedStyledAttributes(len);
1368
1369        // XXX note that for now we only work with compiled XML files.
1370        // To support generic XML files we will need to manually parse
1371        // out the attributes from the XML file (applying type information
1372        // contained in the resources and such).
1373        XmlBlock.Parser parser = (XmlBlock.Parser)set;
1374        mAssets.retrieveAttributes(parser.mParseState, attrs,
1375                array.mData, array.mIndices);
1376
1377        array.mRsrcs = attrs;
1378        array.mXml = parser;
1379
1380        return array;
1381    }
1382
1383    /**
1384     * Store the newly updated configuration.
1385     */
1386    public void updateConfiguration(Configuration config,
1387            DisplayMetrics metrics) {
1388        updateConfiguration(config, metrics, null);
1389    }
1390
1391    /**
1392     * @hide
1393     */
1394    public void updateConfiguration(Configuration config,
1395            DisplayMetrics metrics, CompatibilityInfo compat) {
1396        synchronized (mTmpValue) {
1397            if (false) {
1398                Slog.i(TAG, "**** Updating config of " + this + ": old config is "
1399                        + mConfiguration + " old compat is " + mCompatibilityInfo);
1400                Slog.i(TAG, "**** Updating config of " + this + ": new config is "
1401                        + config + " new compat is " + compat);
1402            }
1403            if (compat != null) {
1404                mCompatibilityInfo = compat;
1405            }
1406            if (metrics != null) {
1407                mMetrics.setTo(metrics);
1408            }
1409            // NOTE: We should re-arrange this code to create a Display
1410            // with the CompatibilityInfo that is used everywhere we deal
1411            // with the display in relation to this app, rather than
1412            // doing the conversion here.  This impl should be okay because
1413            // we make sure to return a compatible display in the places
1414            // where there are public APIs to retrieve the display...  but
1415            // it would be cleaner and more maintainble to just be
1416            // consistently dealing with a compatible display everywhere in
1417            // the framework.
1418            if (mCompatibilityInfo != null) {
1419                mCompatibilityInfo.applyToDisplayMetrics(mMetrics);
1420            }
1421            int configChanges = 0xfffffff;
1422            if (config != null) {
1423                mTmpConfig.setTo(config);
1424                if (mCompatibilityInfo != null) {
1425                    mCompatibilityInfo.applyToConfiguration(mTmpConfig);
1426                }
1427                if (mTmpConfig.locale == null) {
1428                    mTmpConfig.locale = Locale.getDefault();
1429                }
1430                configChanges = mConfiguration.updateFrom(mTmpConfig);
1431                configChanges = ActivityInfo.activityInfoConfigToNative(configChanges);
1432            }
1433            if (mConfiguration.locale == null) {
1434                mConfiguration.locale = Locale.getDefault();
1435            }
1436            mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale;
1437
1438            String locale = null;
1439            if (mConfiguration.locale != null) {
1440                locale = mConfiguration.locale.getLanguage();
1441                if (mConfiguration.locale.getCountry() != null) {
1442                    locale += "-" + mConfiguration.locale.getCountry();
1443                }
1444            }
1445            int width, height;
1446            if (mMetrics.widthPixels >= mMetrics.heightPixels) {
1447                width = mMetrics.widthPixels;
1448                height = mMetrics.heightPixels;
1449            } else {
1450                //noinspection SuspiciousNameCombination
1451                width = mMetrics.heightPixels;
1452                //noinspection SuspiciousNameCombination
1453                height = mMetrics.widthPixels;
1454            }
1455            int keyboardHidden = mConfiguration.keyboardHidden;
1456            if (keyboardHidden == Configuration.KEYBOARDHIDDEN_NO
1457                    && mConfiguration.hardKeyboardHidden
1458                            == Configuration.HARDKEYBOARDHIDDEN_YES) {
1459                keyboardHidden = Configuration.KEYBOARDHIDDEN_SOFT;
1460            }
1461            mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
1462                    locale, mConfiguration.orientation,
1463                    mConfiguration.touchscreen,
1464                    (int)(mMetrics.density*160), mConfiguration.keyboard,
1465                    keyboardHidden, mConfiguration.navigation, width, height,
1466                    mConfiguration.smallestScreenWidthDp,
1467                    mConfiguration.screenWidthDp, mConfiguration.screenHeightDp,
1468                    mConfiguration.screenLayout, mConfiguration.uiMode,
1469                    Build.VERSION.RESOURCES_SDK_INT);
1470
1471            if (DEBUG_CONFIG) {
1472                Slog.i(TAG, "**** Updating config of " + this + ": final config is " + mConfiguration
1473                        + " final compat is " + mCompatibilityInfo);
1474            }
1475
1476            clearDrawableCache(mDrawableCache, configChanges);
1477            clearDrawableCache(mColorDrawableCache, configChanges);
1478
1479            mColorStateListCache.clear();
1480
1481            flushLayoutCache();
1482        }
1483        synchronized (mSync) {
1484            if (mPluralRule != null) {
1485                mPluralRule = NativePluralRules.forLocale(config.locale);
1486            }
1487        }
1488    }
1489
1490    private void clearDrawableCache(
1491            LongSparseArray<WeakReference<ConstantState>> cache,
1492            int configChanges) {
1493        int N = cache.size();
1494        if (DEBUG_CONFIG) {
1495            Log.d(TAG, "Cleaning up drawables config changes: 0x"
1496                    + Integer.toHexString(configChanges));
1497        }
1498        for (int i=0; i<N; i++) {
1499            WeakReference<Drawable.ConstantState> ref = cache.valueAt(i);
1500            if (ref != null) {
1501                Drawable.ConstantState cs = ref.get();
1502                if (cs != null) {
1503                    if (Configuration.needNewResources(
1504                            configChanges, cs.getChangingConfigurations())) {
1505                        if (DEBUG_CONFIG) {
1506                            Log.d(TAG, "FLUSHING #0x"
1507                                    + Long.toHexString(mDrawableCache.keyAt(i))
1508                                    + " / " + cs + " with changes: 0x"
1509                                    + Integer.toHexString(cs.getChangingConfigurations()));
1510                        }
1511                        cache.setValueAt(i, null);
1512                    } else if (DEBUG_CONFIG) {
1513                        Log.d(TAG, "(Keeping #0x"
1514                                + Long.toHexString(cache.keyAt(i))
1515                                + " / " + cs + " with changes: 0x"
1516                                + Integer.toHexString(cs.getChangingConfigurations())
1517                                + ")");
1518                    }
1519                }
1520            }
1521        }
1522    }
1523
1524    /**
1525     * Update the system resources configuration if they have previously
1526     * been initialized.
1527     *
1528     * @hide
1529     */
1530    public static void updateSystemConfiguration(Configuration config, DisplayMetrics metrics,
1531            CompatibilityInfo compat) {
1532        if (mSystem != null) {
1533            mSystem.updateConfiguration(config, metrics, compat);
1534            //Log.i(TAG, "Updated system resources " + mSystem
1535            //        + ": " + mSystem.getConfiguration());
1536        }
1537    }
1538
1539    /**
1540     * @hide
1541     */
1542    public static void updateSystemConfiguration(Configuration config, DisplayMetrics metrics) {
1543        updateSystemConfiguration(config, metrics, null);
1544    }
1545
1546    /**
1547     * Return the current display metrics that are in effect for this resource
1548     * object.  The returned object should be treated as read-only.
1549     *
1550     * @return The resource's current display metrics.
1551     */
1552    public DisplayMetrics getDisplayMetrics() {
1553        if (DEBUG_CONFIG) Slog.v(TAG, "Returning DisplayMetrics: " + mMetrics.widthPixels
1554                + "x" + mMetrics.heightPixels + " " + mMetrics.density);
1555        return mMetrics;
1556    }
1557
1558    /**
1559     * Return the current configuration that is in effect for this resource
1560     * object.  The returned object should be treated as read-only.
1561     *
1562     * @return The resource's current configuration.
1563     */
1564    public Configuration getConfiguration() {
1565        return mConfiguration;
1566    }
1567
1568    /**
1569     * Return the compatibility mode information for the application.
1570     * The returned object should be treated as read-only.
1571     *
1572     * @return compatibility info.
1573     * @hide
1574     */
1575    public CompatibilityInfo getCompatibilityInfo() {
1576        return mCompatibilityInfo != null ? mCompatibilityInfo
1577                : CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
1578    }
1579
1580    /**
1581     * This is just for testing.
1582     * @hide
1583     */
1584    public void setCompatibilityInfo(CompatibilityInfo ci) {
1585        mCompatibilityInfo = ci;
1586        updateConfiguration(mConfiguration, mMetrics);
1587    }
1588
1589    /**
1590     * Return a resource identifier for the given resource name.  A fully
1591     * qualified resource name is of the form "package:type/entry".  The first
1592     * two components (package and type) are optional if defType and
1593     * defPackage, respectively, are specified here.
1594     *
1595     * <p>Note: use of this function is discouraged.  It is much more
1596     * efficient to retrieve resources by identifier than by name.
1597     *
1598     * @param name The name of the desired resource.
1599     * @param defType Optional default resource type to find, if "type/" is
1600     *                not included in the name.  Can be null to require an
1601     *                explicit type.
1602     * @param defPackage Optional default package to find, if "package:" is
1603     *                   not included in the name.  Can be null to require an
1604     *                   explicit package.
1605     *
1606     * @return int The associated resource identifier.  Returns 0 if no such
1607     *         resource was found.  (0 is not a valid resource ID.)
1608     */
1609    public int getIdentifier(String name, String defType, String defPackage) {
1610        try {
1611            return Integer.parseInt(name);
1612        } catch (Exception e) {
1613            // Ignore
1614        }
1615        return mAssets.getResourceIdentifier(name, defType, defPackage);
1616    }
1617
1618    /**
1619     * Return the full name for a given resource identifier.  This name is
1620     * a single string of the form "package:type/entry".
1621     *
1622     * @param resid The resource identifier whose name is to be retrieved.
1623     *
1624     * @return A string holding the name of the resource.
1625     *
1626     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1627     *
1628     * @see #getResourcePackageName
1629     * @see #getResourceTypeName
1630     * @see #getResourceEntryName
1631     */
1632    public String getResourceName(int resid) throws NotFoundException {
1633        String str = mAssets.getResourceName(resid);
1634        if (str != null) return str;
1635        throw new NotFoundException("Unable to find resource ID #0x"
1636                + Integer.toHexString(resid));
1637    }
1638
1639    /**
1640     * Return the package name for a given resource identifier.
1641     *
1642     * @param resid The resource identifier whose package name is to be
1643     * retrieved.
1644     *
1645     * @return A string holding the package name of the resource.
1646     *
1647     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1648     *
1649     * @see #getResourceName
1650     */
1651    public String getResourcePackageName(int resid) throws NotFoundException {
1652        String str = mAssets.getResourcePackageName(resid);
1653        if (str != null) return str;
1654        throw new NotFoundException("Unable to find resource ID #0x"
1655                + Integer.toHexString(resid));
1656    }
1657
1658    /**
1659     * Return the type name for a given resource identifier.
1660     *
1661     * @param resid The resource identifier whose type name is to be
1662     * retrieved.
1663     *
1664     * @return A string holding the type name of the resource.
1665     *
1666     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1667     *
1668     * @see #getResourceName
1669     */
1670    public String getResourceTypeName(int resid) throws NotFoundException {
1671        String str = mAssets.getResourceTypeName(resid);
1672        if (str != null) return str;
1673        throw new NotFoundException("Unable to find resource ID #0x"
1674                + Integer.toHexString(resid));
1675    }
1676
1677    /**
1678     * Return the entry name for a given resource identifier.
1679     *
1680     * @param resid The resource identifier whose entry name is to be
1681     * retrieved.
1682     *
1683     * @return A string holding the entry name of the resource.
1684     *
1685     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1686     *
1687     * @see #getResourceName
1688     */
1689    public String getResourceEntryName(int resid) throws NotFoundException {
1690        String str = mAssets.getResourceEntryName(resid);
1691        if (str != null) return str;
1692        throw new NotFoundException("Unable to find resource ID #0x"
1693                + Integer.toHexString(resid));
1694    }
1695
1696    /**
1697     * Parse a series of {@link android.R.styleable#Extra &lt;extra&gt;} tags from
1698     * an XML file.  You call this when you are at the parent tag of the
1699     * extra tags, and it will return once all of the child tags have been parsed.
1700     * This will call {@link #parseBundleExtra} for each extra tag encountered.
1701     *
1702     * @param parser The parser from which to retrieve the extras.
1703     * @param outBundle A Bundle in which to place all parsed extras.
1704     * @throws XmlPullParserException
1705     * @throws IOException
1706     */
1707    public void parseBundleExtras(XmlResourceParser parser, Bundle outBundle)
1708            throws XmlPullParserException, IOException {
1709        int outerDepth = parser.getDepth();
1710        int type;
1711        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1712               && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
1713            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1714                continue;
1715            }
1716
1717            String nodeName = parser.getName();
1718            if (nodeName.equals("extra")) {
1719                parseBundleExtra("extra", parser, outBundle);
1720                XmlUtils.skipCurrentTag(parser);
1721
1722            } else {
1723                XmlUtils.skipCurrentTag(parser);
1724            }
1725        }
1726    }
1727
1728    /**
1729     * Parse a name/value pair out of an XML tag holding that data.  The
1730     * AttributeSet must be holding the data defined by
1731     * {@link android.R.styleable#Extra}.  The following value types are supported:
1732     * <ul>
1733     * <li> {@link TypedValue#TYPE_STRING}:
1734     * {@link Bundle#putCharSequence Bundle.putCharSequence()}
1735     * <li> {@link TypedValue#TYPE_INT_BOOLEAN}:
1736     * {@link Bundle#putCharSequence Bundle.putBoolean()}
1737     * <li> {@link TypedValue#TYPE_FIRST_INT}-{@link TypedValue#TYPE_LAST_INT}:
1738     * {@link Bundle#putCharSequence Bundle.putBoolean()}
1739     * <li> {@link TypedValue#TYPE_FLOAT}:
1740     * {@link Bundle#putCharSequence Bundle.putFloat()}
1741     * </ul>
1742     *
1743     * @param tagName The name of the tag these attributes come from; this is
1744     * only used for reporting error messages.
1745     * @param attrs The attributes from which to retrieve the name/value pair.
1746     * @param outBundle The Bundle in which to place the parsed value.
1747     * @throws XmlPullParserException If the attributes are not valid.
1748     */
1749    public void parseBundleExtra(String tagName, AttributeSet attrs,
1750            Bundle outBundle) throws XmlPullParserException {
1751        TypedArray sa = obtainAttributes(attrs,
1752                com.android.internal.R.styleable.Extra);
1753
1754        String name = sa.getString(
1755                com.android.internal.R.styleable.Extra_name);
1756        if (name == null) {
1757            sa.recycle();
1758            throw new XmlPullParserException("<" + tagName
1759                    + "> requires an android:name attribute at "
1760                    + attrs.getPositionDescription());
1761        }
1762
1763        TypedValue v = sa.peekValue(
1764                com.android.internal.R.styleable.Extra_value);
1765        if (v != null) {
1766            if (v.type == TypedValue.TYPE_STRING) {
1767                CharSequence cs = v.coerceToString();
1768                outBundle.putCharSequence(name, cs);
1769            } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) {
1770                outBundle.putBoolean(name, v.data != 0);
1771            } else if (v.type >= TypedValue.TYPE_FIRST_INT
1772                    && v.type <= TypedValue.TYPE_LAST_INT) {
1773                outBundle.putInt(name, v.data);
1774            } else if (v.type == TypedValue.TYPE_FLOAT) {
1775                outBundle.putFloat(name, v.getFloat());
1776            } else {
1777                sa.recycle();
1778                throw new XmlPullParserException("<" + tagName
1779                        + "> only supports string, integer, float, color, and boolean at "
1780                        + attrs.getPositionDescription());
1781            }
1782        } else {
1783            sa.recycle();
1784            throw new XmlPullParserException("<" + tagName
1785                    + "> requires an android:value or android:resource attribute at "
1786                    + attrs.getPositionDescription());
1787        }
1788
1789        sa.recycle();
1790    }
1791
1792    /**
1793     * Retrieve underlying AssetManager storage for these resources.
1794     */
1795    public final AssetManager getAssets() {
1796        return mAssets;
1797    }
1798
1799    /**
1800     * Call this to remove all cached loaded layout resources from the
1801     * Resources object.  Only intended for use with performance testing
1802     * tools.
1803     */
1804    public final void flushLayoutCache() {
1805        synchronized (mCachedXmlBlockIds) {
1806            // First see if this block is in our cache.
1807            final int num = mCachedXmlBlockIds.length;
1808            for (int i=0; i<num; i++) {
1809                mCachedXmlBlockIds[i] = -0;
1810                XmlBlock oldBlock = mCachedXmlBlocks[i];
1811                if (oldBlock != null) {
1812                    oldBlock.close();
1813                }
1814                mCachedXmlBlocks[i] = null;
1815            }
1816        }
1817    }
1818
1819    /**
1820     * Start preloading of resource data using this Resources object.  Only
1821     * for use by the zygote process for loading common system resources.
1822     * {@hide}
1823     */
1824    public final void startPreloading() {
1825        synchronized (mSync) {
1826            if (mPreloaded) {
1827                throw new IllegalStateException("Resources already preloaded");
1828            }
1829            mPreloaded = true;
1830            mPreloading = true;
1831        }
1832    }
1833
1834    /**
1835     * Called by zygote when it is done preloading resources, to change back
1836     * to normal Resources operation.
1837     */
1838    public final void finishPreloading() {
1839        if (mPreloading) {
1840            mPreloading = false;
1841            flushLayoutCache();
1842        }
1843    }
1844
1845    /*package*/ Drawable loadDrawable(TypedValue value, int id)
1846            throws NotFoundException {
1847
1848        if (TRACE_FOR_PRELOAD) {
1849            // Log only framework resources
1850            if ((id >>> 24) == 0x1) {
1851                final String name = getResourceName(id);
1852                if (name != null) android.util.Log.d("PreloadDrawable", name);
1853            }
1854        }
1855
1856        final long key = (((long) value.assetCookie) << 32) | value.data;
1857        boolean isColorDrawable = false;
1858        if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
1859                value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
1860            isColorDrawable = true;
1861        }
1862        Drawable dr = getCachedDrawable(isColorDrawable ? mColorDrawableCache : mDrawableCache, key);
1863
1864        if (dr != null) {
1865            return dr;
1866        }
1867
1868        Drawable.ConstantState cs = isColorDrawable ?
1869                sPreloadedColorDrawables.get(key) : sPreloadedDrawables.get(key);
1870        if (cs != null) {
1871            dr = cs.newDrawable(this);
1872        } else {
1873            if (isColorDrawable) {
1874                dr = new ColorDrawable(value.data);
1875            }
1876
1877            if (dr == null) {
1878                if (value.string == null) {
1879                    throw new NotFoundException(
1880                            "Resource is not a Drawable (color or path): " + value);
1881                }
1882
1883                String file = value.string.toString();
1884
1885                if (TRACE_FOR_MISS_PRELOAD) {
1886                    // Log only framework resources
1887                    if ((id >>> 24) == 0x1) {
1888                        final String name = getResourceName(id);
1889                        if (name != null) android.util.Log.d(TAG, "Loading framework drawable #"
1890                                + Integer.toHexString(id) + ": " + name
1891                                + " at " + file);
1892                    }
1893                }
1894
1895                if (DEBUG_LOAD) Log.v(TAG, "Loading drawable for cookie "
1896                        + value.assetCookie + ": " + file);
1897
1898                if (file.endsWith(".xml")) {
1899                    try {
1900                        XmlResourceParser rp = loadXmlResourceParser(
1901                                file, id, value.assetCookie, "drawable");
1902                        dr = Drawable.createFromXml(this, rp);
1903                        rp.close();
1904                    } catch (Exception e) {
1905                        NotFoundException rnf = new NotFoundException(
1906                            "File " + file + " from drawable resource ID #0x"
1907                            + Integer.toHexString(id));
1908                        rnf.initCause(e);
1909                        throw rnf;
1910                    }
1911
1912                } else {
1913                    try {
1914                        InputStream is = mAssets.openNonAsset(
1915                                value.assetCookie, file, AssetManager.ACCESS_STREAMING);
1916        //                System.out.println("Opened file " + file + ": " + is);
1917                        dr = Drawable.createFromResourceStream(this, value, is,
1918                                file, null);
1919                        is.close();
1920        //                System.out.println("Created stream: " + dr);
1921                    } catch (Exception e) {
1922                        NotFoundException rnf = new NotFoundException(
1923                            "File " + file + " from drawable resource ID #0x"
1924                            + Integer.toHexString(id));
1925                        rnf.initCause(e);
1926                        throw rnf;
1927                    }
1928                }
1929            }
1930        }
1931
1932        if (dr != null) {
1933            dr.setChangingConfigurations(value.changingConfigurations);
1934            cs = dr.getConstantState();
1935            if (cs != null) {
1936                if (mPreloading) {
1937                    if (isColorDrawable) {
1938                        sPreloadedColorDrawables.put(key, cs);
1939                    } else {
1940                        sPreloadedDrawables.put(key, cs);
1941                    }
1942                } else {
1943                    synchronized (mTmpValue) {
1944                        //Log.i(TAG, "Saving cached drawable @ #" +
1945                        //        Integer.toHexString(key.intValue())
1946                        //        + " in " + this + ": " + cs);
1947                        if (isColorDrawable) {
1948                            mColorDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs));
1949                        } else {
1950                            mDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs));
1951                        }
1952                    }
1953                }
1954            }
1955        }
1956
1957        return dr;
1958    }
1959
1960    private Drawable getCachedDrawable(
1961            LongSparseArray<WeakReference<ConstantState>> drawableCache,
1962            long key) {
1963        synchronized (mTmpValue) {
1964            WeakReference<Drawable.ConstantState> wr = drawableCache.get(key);
1965            if (wr != null) {   // we have the key
1966                Drawable.ConstantState entry = wr.get();
1967                if (entry != null) {
1968                    //Log.i(TAG, "Returning cached drawable @ #" +
1969                    //        Integer.toHexString(((Integer)key).intValue())
1970                    //        + " in " + this + ": " + entry);
1971                    return entry.newDrawable(this);
1972                }
1973                else {  // our entry has been purged
1974                    drawableCache.delete(key);
1975                }
1976            }
1977        }
1978        return null;
1979    }
1980
1981    /*package*/ ColorStateList loadColorStateList(TypedValue value, int id)
1982            throws NotFoundException {
1983        if (TRACE_FOR_PRELOAD) {
1984            // Log only framework resources
1985            if ((id >>> 24) == 0x1) {
1986                final String name = getResourceName(id);
1987                if (name != null) android.util.Log.d("PreloadColorStateList", name);
1988            }
1989        }
1990
1991        final long key = (((long) value.assetCookie) << 32) | value.data;
1992
1993        ColorStateList csl;
1994
1995        if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
1996                value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
1997
1998            csl = sPreloadedColorStateLists.get(key);
1999            if (csl != null) {
2000                return csl;
2001            }
2002
2003            csl = ColorStateList.valueOf(value.data);
2004            if (mPreloading) {
2005                sPreloadedColorStateLists.put(key, csl);
2006            }
2007
2008            return csl;
2009        }
2010
2011        csl = getCachedColorStateList(key);
2012        if (csl != null) {
2013            return csl;
2014        }
2015
2016        csl = sPreloadedColorStateLists.get(key);
2017        if (csl != null) {
2018            return csl;
2019        }
2020
2021        if (value.string == null) {
2022            throw new NotFoundException(
2023                    "Resource is not a ColorStateList (color or path): " + value);
2024        }
2025
2026        String file = value.string.toString();
2027
2028        if (file.endsWith(".xml")) {
2029            try {
2030                XmlResourceParser rp = loadXmlResourceParser(
2031                        file, id, value.assetCookie, "colorstatelist");
2032                csl = ColorStateList.createFromXml(this, rp);
2033                rp.close();
2034            } catch (Exception e) {
2035                NotFoundException rnf = new NotFoundException(
2036                    "File " + file + " from color state list resource ID #0x"
2037                    + Integer.toHexString(id));
2038                rnf.initCause(e);
2039                throw rnf;
2040            }
2041        } else {
2042            throw new NotFoundException(
2043                    "File " + file + " from drawable resource ID #0x"
2044                    + Integer.toHexString(id) + ": .xml extension required");
2045        }
2046
2047        if (csl != null) {
2048            if (mPreloading) {
2049                sPreloadedColorStateLists.put(key, csl);
2050            } else {
2051                synchronized (mTmpValue) {
2052                    //Log.i(TAG, "Saving cached color state list @ #" +
2053                    //        Integer.toHexString(key.intValue())
2054                    //        + " in " + this + ": " + csl);
2055                    mColorStateListCache.put(key, new WeakReference<ColorStateList>(csl));
2056                }
2057            }
2058        }
2059
2060        return csl;
2061    }
2062
2063    private ColorStateList getCachedColorStateList(long key) {
2064        synchronized (mTmpValue) {
2065            WeakReference<ColorStateList> wr = mColorStateListCache.get(key);
2066            if (wr != null) {   // we have the key
2067                ColorStateList entry = wr.get();
2068                if (entry != null) {
2069                    //Log.i(TAG, "Returning cached color state list @ #" +
2070                    //        Integer.toHexString(((Integer)key).intValue())
2071                    //        + " in " + this + ": " + entry);
2072                    return entry;
2073                } else {  // our entry has been purged
2074                    mColorStateListCache.delete(key);
2075                }
2076            }
2077        }
2078        return null;
2079    }
2080
2081    /*package*/ XmlResourceParser loadXmlResourceParser(int id, String type)
2082            throws NotFoundException {
2083        synchronized (mTmpValue) {
2084            TypedValue value = mTmpValue;
2085            getValue(id, value, true);
2086            if (value.type == TypedValue.TYPE_STRING) {
2087                return loadXmlResourceParser(value.string.toString(), id,
2088                        value.assetCookie, type);
2089            }
2090            throw new NotFoundException(
2091                    "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
2092                    + Integer.toHexString(value.type) + " is not valid");
2093        }
2094    }
2095
2096    /*package*/ XmlResourceParser loadXmlResourceParser(String file, int id,
2097            int assetCookie, String type) throws NotFoundException {
2098        if (id != 0) {
2099            try {
2100                // These may be compiled...
2101                synchronized (mCachedXmlBlockIds) {
2102                    // First see if this block is in our cache.
2103                    final int num = mCachedXmlBlockIds.length;
2104                    for (int i=0; i<num; i++) {
2105                        if (mCachedXmlBlockIds[i] == id) {
2106                            //System.out.println("**** REUSING XML BLOCK!  id="
2107                            //                   + id + ", index=" + i);
2108                            return mCachedXmlBlocks[i].newParser();
2109                        }
2110                    }
2111
2112                    // Not in the cache, create a new block and put it at
2113                    // the next slot in the cache.
2114                    XmlBlock block = mAssets.openXmlBlockAsset(
2115                            assetCookie, file);
2116                    if (block != null) {
2117                        int pos = mLastCachedXmlBlockIndex+1;
2118                        if (pos >= num) pos = 0;
2119                        mLastCachedXmlBlockIndex = pos;
2120                        XmlBlock oldBlock = mCachedXmlBlocks[pos];
2121                        if (oldBlock != null) {
2122                            oldBlock.close();
2123                        }
2124                        mCachedXmlBlockIds[pos] = id;
2125                        mCachedXmlBlocks[pos] = block;
2126                        //System.out.println("**** CACHING NEW XML BLOCK!  id="
2127                        //                   + id + ", index=" + pos);
2128                        return block.newParser();
2129                    }
2130                }
2131            } catch (Exception e) {
2132                NotFoundException rnf = new NotFoundException(
2133                        "File " + file + " from xml type " + type + " resource ID #0x"
2134                        + Integer.toHexString(id));
2135                rnf.initCause(e);
2136                throw rnf;
2137            }
2138        }
2139
2140        throw new NotFoundException(
2141                "File " + file + " from xml type " + type + " resource ID #0x"
2142                + Integer.toHexString(id));
2143    }
2144
2145    private TypedArray getCachedStyledAttributes(int len) {
2146        synchronized (mTmpValue) {
2147            TypedArray attrs = mCachedStyledAttributes;
2148            if (attrs != null) {
2149                mCachedStyledAttributes = null;
2150                if (DEBUG_ATTRIBUTES_CACHE) {
2151                    mLastRetrievedAttrs = new RuntimeException("here");
2152                    mLastRetrievedAttrs.fillInStackTrace();
2153                }
2154
2155                attrs.mLength = len;
2156                int fullLen = len * AssetManager.STYLE_NUM_ENTRIES;
2157                if (attrs.mData.length >= fullLen) {
2158                    return attrs;
2159                }
2160                attrs.mData = new int[fullLen];
2161                attrs.mIndices = new int[1+len];
2162                return attrs;
2163            }
2164            if (DEBUG_ATTRIBUTES_CACHE) {
2165                RuntimeException here = new RuntimeException("here");
2166                here.fillInStackTrace();
2167                if (mLastRetrievedAttrs != null) {
2168                    Log.i(TAG, "Allocated new TypedArray of " + len + " in " + this, here);
2169                    Log.i(TAG, "Last retrieved attributes here", mLastRetrievedAttrs);
2170                }
2171                mLastRetrievedAttrs = here;
2172            }
2173            return new TypedArray(this,
2174                    new int[len*AssetManager.STYLE_NUM_ENTRIES],
2175                    new int[1+len], len);
2176        }
2177    }
2178
2179    private Resources() {
2180        mAssets = AssetManager.getSystem();
2181        // NOTE: Intentionally leaving this uninitialized (all values set
2182        // to zero), so that anyone who tries to do something that requires
2183        // metrics will get a very wrong value.
2184        mConfiguration.setToDefaults();
2185        mMetrics.setToDefaults();
2186        updateConfiguration(null, null);
2187        mAssets.ensureStringBlocks();
2188        mCompatibilityInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
2189    }
2190}
2191