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