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