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