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