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