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