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