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