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