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