Resources.java revision 7e0aaae3db11e0f23dca8c4cddff261f0132266c
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     */
748    public Drawable getDrawable(int id) throws NotFoundException {
749        final Drawable d = getDrawable(id, null);
750        if (d != null && d.canApplyTheme()) {
751            Log.w(TAG, "Drawable " + getResourceName(id) + " has unresolved theme "
752                    + "attributes! Consider using Resources.getDrawable(int, Theme) or "
753                    + "Context.getDrawable(int).", new RuntimeException());
754        }
755        return d;
756    }
757
758    /**
759     * Return a drawable object associated with a particular resource ID and
760     * styled for the specified theme. Various types of objects will be
761     * returned depending on the underlying resource -- for example, a solid
762     * color, PNG image, scalable image, etc.
763     *
764     * @param id The desired resource identifier, as generated by the aapt
765     *           tool. This integer encodes the package, type, and resource
766     *           entry. The value 0 is an invalid identifier.
767     * @param theme The theme used to style the drawable attributes, may be {@code null}.
768     * @return Drawable An object that can be used to draw this resource.
769     * @throws NotFoundException Throws NotFoundException if the given ID does
770     *         not exist.
771     */
772    public Drawable getDrawable(int id, @Nullable Theme theme) throws NotFoundException {
773        TypedValue value;
774        synchronized (mAccessLock) {
775            value = mTmpValue;
776            if (value == null) {
777                value = new TypedValue();
778            } else {
779                mTmpValue = null;
780            }
781            getValue(id, value, true);
782        }
783        final Drawable res = loadDrawable(value, id, theme);
784        synchronized (mAccessLock) {
785            if (mTmpValue == null) {
786                mTmpValue = value;
787            }
788        }
789        return res;
790    }
791
792    /**
793     * Return a drawable object associated with a particular resource ID for the
794     * given screen density in DPI. This will set the drawable's density to be
795     * the device's density multiplied by the ratio of actual drawable density
796     * to requested density. This allows the drawable to be scaled up to the
797     * correct size if needed. Various types of objects will be returned
798     * depending on the underlying resource -- for example, a solid color, PNG
799     * image, scalable image, etc. The Drawable API hides these implementation
800     * details.
801     *
802     * <p class="note"><strong>Note:</strong> To obtain a themed drawable, use
803     * {@link android.content.Context#getDrawable(int) Context.getDrawable(int)}
804     * or {@link #getDrawableForDensity(int, int, Theme)} passing the desired
805     * theme.</p>
806     *
807     * @param id The desired resource identifier, as generated by the aapt tool.
808     *            This integer encodes the package, type, and resource entry.
809     *            The value 0 is an invalid identifier.
810     * @param density the desired screen density indicated by the resource as
811     *            found in {@link DisplayMetrics}.
812     * @return Drawable An object that can be used to draw this resource.
813     * @throws NotFoundException Throws NotFoundException if the given ID does
814     *             not exist.
815     * @see #getDrawableForDensity(int, int, Theme)
816     */
817    public Drawable getDrawableForDensity(int id, int density) throws NotFoundException {
818        return getDrawableForDensity(id, density, null);
819    }
820
821    /**
822     * Return a drawable object associated with a particular resource ID for the
823     * given screen density in DPI and styled for the specified theme.
824     *
825     * @param id The desired resource identifier, as generated by the aapt tool.
826     *            This integer encodes the package, type, and resource entry.
827     *            The value 0 is an invalid identifier.
828     * @param density The desired screen density indicated by the resource as
829     *            found in {@link DisplayMetrics}.
830     * @param theme The theme used to style the drawable attributes, may be {@code null}.
831     * @return Drawable An object that can be used to draw this resource.
832     * @throws NotFoundException Throws NotFoundException if the given ID does
833     *             not exist.
834     */
835    public Drawable getDrawableForDensity(int id, int density, @Nullable Theme theme) {
836        TypedValue value;
837        synchronized (mAccessLock) {
838            value = mTmpValue;
839            if (value == null) {
840                value = new TypedValue();
841            } else {
842                mTmpValue = null;
843            }
844            getValueForDensity(id, density, value, true);
845
846            /*
847             * Pretend the requested density is actually the display density. If
848             * the drawable returned is not the requested density, then force it
849             * to be scaled later by dividing its density by the ratio of
850             * requested density to actual device density. Drawables that have
851             * undefined density or no density don't need to be handled here.
852             */
853            if (value.density > 0 && value.density != TypedValue.DENSITY_NONE) {
854                if (value.density == density) {
855                    value.density = mMetrics.densityDpi;
856                } else {
857                    value.density = (value.density * mMetrics.densityDpi) / density;
858                }
859            }
860        }
861
862        final Drawable res = loadDrawable(value, id, theme);
863        synchronized (mAccessLock) {
864            if (mTmpValue == null) {
865                mTmpValue = value;
866            }
867        }
868        return res;
869    }
870
871    /**
872     * Return a movie object associated with the particular resource ID.
873     * @param id The desired resource identifier, as generated by the aapt
874     *           tool. This integer encodes the package, type, and resource
875     *           entry. The value 0 is an invalid identifier.
876     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
877     *
878     */
879    public Movie getMovie(int id) throws NotFoundException {
880        InputStream is = openRawResource(id);
881        Movie movie = Movie.decodeStream(is);
882        try {
883            is.close();
884        }
885        catch (java.io.IOException e) {
886            // don't care, since the return value is valid
887        }
888        return movie;
889    }
890
891    /**
892     * Return a color integer associated with a particular resource ID.
893     * If the resource holds a complex
894     * {@link android.content.res.ColorStateList}, then the default color from
895     * the set is returned.
896     *
897     * @param id The desired resource identifier, as generated by the aapt
898     *           tool. This integer encodes the package, type, and resource
899     *           entry. The value 0 is an invalid identifier.
900     *
901     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
902     *
903     * @return Returns a single color value in the form 0xAARRGGBB.
904     */
905    public int getColor(int id) throws NotFoundException {
906        TypedValue value;
907        synchronized (mAccessLock) {
908            value = mTmpValue;
909            if (value == null) {
910                value = new TypedValue();
911            }
912            getValue(id, value, true);
913            if (value.type >= TypedValue.TYPE_FIRST_INT
914                && value.type <= TypedValue.TYPE_LAST_INT) {
915                mTmpValue = value;
916                return value.data;
917            } else if (value.type != TypedValue.TYPE_STRING) {
918                throw new NotFoundException(
919                    "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
920                    + Integer.toHexString(value.type) + " is not valid");
921            }
922            mTmpValue = null;
923        }
924        ColorStateList csl = loadColorStateList(value, id);
925        synchronized (mAccessLock) {
926            if (mTmpValue == null) {
927                mTmpValue = value;
928            }
929        }
930        return csl.getDefaultColor();
931    }
932
933    /**
934     * Return a color state list associated with a particular resource ID.  The
935     * resource may contain either a single raw color value, or a complex
936     * {@link android.content.res.ColorStateList} holding multiple possible colors.
937     *
938     * @param id The desired resource identifier of a {@link ColorStateList},
939     *        as generated by the aapt tool. This integer encodes the package, type, and resource
940     *        entry. The value 0 is an invalid identifier.
941     *
942     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
943     *
944     * @return Returns a ColorStateList object containing either a single
945     * solid color or multiple colors that can be selected based on a state.
946     */
947    public ColorStateList getColorStateList(int id) throws NotFoundException {
948        TypedValue value;
949        synchronized (mAccessLock) {
950            value = mTmpValue;
951            if (value == null) {
952                value = new TypedValue();
953            } else {
954                mTmpValue = null;
955            }
956            getValue(id, value, true);
957        }
958        ColorStateList res = loadColorStateList(value, id);
959        synchronized (mAccessLock) {
960            if (mTmpValue == null) {
961                mTmpValue = value;
962            }
963        }
964        return res;
965    }
966
967    /**
968     * Return a boolean associated with a particular resource ID.  This can be
969     * used with any integral resource value, and will return true if it is
970     * non-zero.
971     *
972     * @param id The desired resource identifier, as generated by the aapt
973     *           tool. This integer encodes the package, type, and resource
974     *           entry. The value 0 is an invalid identifier.
975     *
976     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
977     *
978     * @return Returns the boolean value contained in the resource.
979     */
980    public boolean getBoolean(int id) throws NotFoundException {
981        synchronized (mAccessLock) {
982            TypedValue value = mTmpValue;
983            if (value == null) {
984                mTmpValue = value = new TypedValue();
985            }
986            getValue(id, value, true);
987            if (value.type >= TypedValue.TYPE_FIRST_INT
988                && value.type <= TypedValue.TYPE_LAST_INT) {
989                return value.data != 0;
990            }
991            throw new NotFoundException(
992                "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
993                + Integer.toHexString(value.type) + " is not valid");
994        }
995    }
996
997    /**
998     * Return an integer associated with a particular resource ID.
999     *
1000     * @param id The desired resource identifier, as generated by the aapt
1001     *           tool. This integer encodes the package, type, and resource
1002     *           entry. The value 0 is an invalid identifier.
1003     *
1004     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1005     *
1006     * @return Returns the integer value contained in the resource.
1007     */
1008    public int getInteger(int id) throws NotFoundException {
1009        synchronized (mAccessLock) {
1010            TypedValue value = mTmpValue;
1011            if (value == null) {
1012                mTmpValue = value = new TypedValue();
1013            }
1014            getValue(id, value, true);
1015            if (value.type >= TypedValue.TYPE_FIRST_INT
1016                && value.type <= TypedValue.TYPE_LAST_INT) {
1017                return value.data;
1018            }
1019            throw new NotFoundException(
1020                "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
1021                + Integer.toHexString(value.type) + " is not valid");
1022        }
1023    }
1024
1025    /**
1026     * Retrieve a floating-point value for a particular resource ID.
1027     *
1028     * @param id The desired resource identifier, as generated by the aapt
1029     *           tool. This integer encodes the package, type, and resource
1030     *           entry. The value 0 is an invalid identifier.
1031     *
1032     * @return Returns the floating-point value contained in the resource.
1033     *
1034     * @throws NotFoundException Throws NotFoundException if the given ID does
1035     *         not exist or is not a floating-point value.
1036     * @hide Pending API council approval.
1037     */
1038    public float getFloat(int id) {
1039        synchronized (mAccessLock) {
1040            TypedValue value = mTmpValue;
1041            if (value == null) {
1042                mTmpValue = value = new TypedValue();
1043            }
1044            getValue(id, value, true);
1045            if (value.type == TypedValue.TYPE_FLOAT) {
1046                return value.getFloat();
1047            }
1048            throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id) + " type #0x"
1049                    + Integer.toHexString(value.type) + " is not valid");
1050        }
1051    }
1052
1053    /**
1054     * Return an XmlResourceParser through which you can read a view layout
1055     * description for the given resource ID.  This parser has limited
1056     * functionality -- in particular, you can't change its input, and only
1057     * the high-level events are available.
1058     *
1059     * <p>This function is really a simple wrapper for calling
1060     * {@link #getXml} with a layout resource.
1061     *
1062     * @param id The desired resource identifier, as generated by the aapt
1063     *           tool. This integer encodes the package, type, and resource
1064     *           entry. The value 0 is an invalid identifier.
1065     *
1066     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1067     *
1068     * @return A new parser object through which you can read
1069     *         the XML data.
1070     *
1071     * @see #getXml
1072     */
1073    public XmlResourceParser getLayout(int id) throws NotFoundException {
1074        return loadXmlResourceParser(id, "layout");
1075    }
1076
1077    /**
1078     * Return an XmlResourceParser through which you can read an animation
1079     * description for the given resource ID.  This parser has limited
1080     * functionality -- in particular, you can't change its input, and only
1081     * the high-level events are available.
1082     *
1083     * <p>This function is really a simple wrapper for calling
1084     * {@link #getXml} with an animation resource.
1085     *
1086     * @param id The desired resource identifier, as generated by the aapt
1087     *           tool. This integer encodes the package, type, and resource
1088     *           entry. The value 0 is an invalid identifier.
1089     *
1090     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1091     *
1092     * @return A new parser object through which you can read
1093     *         the XML data.
1094     *
1095     * @see #getXml
1096     */
1097    public XmlResourceParser getAnimation(int id) throws NotFoundException {
1098        return loadXmlResourceParser(id, "anim");
1099    }
1100
1101    /**
1102     * Return an XmlResourceParser through which you can read a generic XML
1103     * resource for the given resource ID.
1104     *
1105     * <p>The XmlPullParser implementation returned here has some limited
1106     * functionality.  In particular, you can't change its input, and only
1107     * high-level parsing events are available (since the document was
1108     * pre-parsed for you at build time, which involved merging text and
1109     * stripping comments).
1110     *
1111     * @param id The desired resource identifier, as generated by the aapt
1112     *           tool. This integer encodes the package, type, and resource
1113     *           entry. The value 0 is an invalid identifier.
1114     *
1115     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1116     *
1117     * @return A new parser object through which you can read
1118     *         the XML data.
1119     *
1120     * @see android.util.AttributeSet
1121     */
1122    public XmlResourceParser getXml(int id) throws NotFoundException {
1123        return loadXmlResourceParser(id, "xml");
1124    }
1125
1126    /**
1127     * Open a data stream for reading a raw resource.  This can only be used
1128     * with resources whose value is the name of an asset files -- that is, it can be
1129     * used to open drawable, sound, and raw resources; it will fail on string
1130     * and color resources.
1131     *
1132     * @param id The resource identifier to open, as generated by the appt
1133     *           tool.
1134     *
1135     * @return InputStream Access to the resource data.
1136     *
1137     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1138     *
1139     */
1140    public InputStream openRawResource(int id) throws NotFoundException {
1141        TypedValue value;
1142        synchronized (mAccessLock) {
1143            value = mTmpValue;
1144            if (value == null) {
1145                value = new TypedValue();
1146            } else {
1147                mTmpValue = null;
1148            }
1149        }
1150        InputStream res = openRawResource(id, value);
1151        synchronized (mAccessLock) {
1152            if (mTmpValue == null) {
1153                mTmpValue = value;
1154            }
1155        }
1156        return res;
1157    }
1158
1159    /**
1160     * Open a data stream for reading a raw resource.  This can only be used
1161     * with resources whose value is the name of an asset file -- that is, it can be
1162     * used to open drawable, sound, and raw resources; it will fail on string
1163     * and color resources.
1164     *
1165     * @param id The resource identifier to open, as generated by the appt tool.
1166     * @param value The TypedValue object to hold the resource information.
1167     *
1168     * @return InputStream Access to the resource data.
1169     *
1170     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1171     */
1172    public InputStream openRawResource(int id, TypedValue value) throws NotFoundException {
1173        getValue(id, value, true);
1174
1175        try {
1176            return mAssets.openNonAsset(value.assetCookie, value.string.toString(),
1177                    AssetManager.ACCESS_STREAMING);
1178        } catch (Exception e) {
1179            NotFoundException rnf = new NotFoundException("File " + value.string.toString() +
1180                    " from drawable resource ID #0x" + Integer.toHexString(id));
1181            rnf.initCause(e);
1182            throw rnf;
1183        }
1184    }
1185
1186    /**
1187     * Open a file descriptor for reading a raw resource.  This can only be used
1188     * with resources whose value is the name of an asset files -- that is, it can be
1189     * used to open drawable, sound, and raw resources; it will fail on string
1190     * and color resources.
1191     *
1192     * <p>This function only works for resources that are stored in the package
1193     * as uncompressed data, which typically includes things like mp3 files
1194     * and png images.
1195     *
1196     * @param id The resource identifier to open, as generated by the appt
1197     *           tool.
1198     *
1199     * @return AssetFileDescriptor A new file descriptor you can use to read
1200     * the resource.  This includes the file descriptor itself, as well as the
1201     * offset and length of data where the resource appears in the file.  A
1202     * null is returned if the file exists but is compressed.
1203     *
1204     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1205     *
1206     */
1207    public AssetFileDescriptor openRawResourceFd(int id) throws NotFoundException {
1208        TypedValue value;
1209        synchronized (mAccessLock) {
1210            value = mTmpValue;
1211            if (value == null) {
1212                value = new TypedValue();
1213            } else {
1214                mTmpValue = null;
1215            }
1216            getValue(id, value, true);
1217        }
1218        try {
1219            return mAssets.openNonAssetFd(
1220                value.assetCookie, value.string.toString());
1221        } catch (Exception e) {
1222            NotFoundException rnf = new NotFoundException(
1223                "File " + value.string.toString()
1224                + " from drawable resource ID #0x"
1225                + Integer.toHexString(id));
1226            rnf.initCause(e);
1227            throw rnf;
1228        } finally {
1229            synchronized (mAccessLock) {
1230                if (mTmpValue == null) {
1231                    mTmpValue = value;
1232                }
1233            }
1234        }
1235    }
1236
1237    /**
1238     * Return the raw data associated with a particular resource ID.
1239     *
1240     * @param id The desired resource identifier, as generated by the aapt
1241     *           tool. This integer encodes the package, type, and resource
1242     *           entry. The value 0 is an invalid identifier.
1243     * @param outValue Object in which to place the resource data.
1244     * @param resolveRefs If true, a resource that is a reference to another
1245     *                    resource will be followed so that you receive the
1246     *                    actual final resource data.  If false, the TypedValue
1247     *                    will be filled in with the reference itself.
1248     *
1249     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1250     *
1251     */
1252    public void getValue(int id, TypedValue outValue, boolean resolveRefs)
1253            throws NotFoundException {
1254        boolean found = mAssets.getResourceValue(id, 0, outValue, resolveRefs);
1255        if (found) {
1256            return;
1257        }
1258        throw new NotFoundException("Resource ID #0x"
1259                                    + Integer.toHexString(id));
1260    }
1261
1262    /**
1263     * Get the raw value associated with a resource with associated density.
1264     *
1265     * @param id resource identifier
1266     * @param density density in DPI
1267     * @param resolveRefs If true, a resource that is a reference to another
1268     *            resource will be followed so that you receive the actual final
1269     *            resource data. If false, the TypedValue will be filled in with
1270     *            the reference itself.
1271     * @throws NotFoundException Throws NotFoundException if the given ID does
1272     *             not exist.
1273     * @see #getValue(String, TypedValue, boolean)
1274     */
1275    public void getValueForDensity(int id, int density, TypedValue outValue, boolean resolveRefs)
1276            throws NotFoundException {
1277        boolean found = mAssets.getResourceValue(id, density, outValue, resolveRefs);
1278        if (found) {
1279            return;
1280        }
1281        throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id));
1282    }
1283
1284    /**
1285     * Return the raw data associated with a particular resource ID.
1286     * See getIdentifier() for information on how names are mapped to resource
1287     * IDs, and getString(int) for information on how string resources are
1288     * retrieved.
1289     *
1290     * <p>Note: use of this function is discouraged.  It is much more
1291     * efficient to retrieve resources by identifier than by name.
1292     *
1293     * @param name The name of the desired resource.  This is passed to
1294     *             getIdentifier() with a default type of "string".
1295     * @param outValue Object in which to place the resource data.
1296     * @param resolveRefs If true, a resource that is a reference to another
1297     *                    resource will be followed so that you receive the
1298     *                    actual final resource data.  If false, the TypedValue
1299     *                    will be filled in with the reference itself.
1300     *
1301     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1302     *
1303     */
1304    public void getValue(String name, TypedValue outValue, boolean resolveRefs)
1305            throws NotFoundException {
1306        int id = getIdentifier(name, "string", null);
1307        if (id != 0) {
1308            getValue(id, outValue, resolveRefs);
1309            return;
1310        }
1311        throw new NotFoundException("String resource name " + name);
1312    }
1313
1314    /**
1315     * This class holds the current attribute values for a particular theme.
1316     * In other words, a Theme is a set of values for resource attributes;
1317     * these are used in conjunction with {@link TypedArray}
1318     * to resolve the final value for an attribute.
1319     *
1320     * <p>The Theme's attributes come into play in two ways: (1) a styled
1321     * attribute can explicit reference a value in the theme through the
1322     * "?themeAttribute" syntax; (2) if no value has been defined for a
1323     * particular styled attribute, as a last resort we will try to find that
1324     * attribute's value in the Theme.
1325     *
1326     * <p>You will normally use the {@link #obtainStyledAttributes} APIs to
1327     * retrieve XML attributes with style and theme information applied.
1328     */
1329    public final class Theme {
1330        /**
1331         * Place new attribute values into the theme.  The style resource
1332         * specified by <var>resid</var> will be retrieved from this Theme's
1333         * resources, its values placed into the Theme object.
1334         *
1335         * <p>The semantics of this function depends on the <var>force</var>
1336         * argument:  If false, only values that are not already defined in
1337         * the theme will be copied from the system resource; otherwise, if
1338         * any of the style's attributes are already defined in the theme, the
1339         * current values in the theme will be overwritten.
1340         *
1341         * @param resId The resource ID of a style resource from which to
1342         *              obtain attribute values.
1343         * @param force If true, values in the style resource will always be
1344         *              used in the theme; otherwise, they will only be used
1345         *              if not already defined in the theme.
1346         */
1347        public void applyStyle(int resId, boolean force) {
1348            AssetManager.applyThemeStyle(mTheme, resId, force);
1349
1350            mThemeResId = resId;
1351            mKey += Integer.toHexString(resId) + (force ? "! " : " ");
1352        }
1353
1354        /**
1355         * Set this theme to hold the same contents as the theme
1356         * <var>other</var>.  If both of these themes are from the same
1357         * Resources object, they will be identical after this function
1358         * returns.  If they are from different Resources, only the resources
1359         * they have in common will be set in this theme.
1360         *
1361         * @param other The existing Theme to copy from.
1362         */
1363        public void setTo(Theme other) {
1364            AssetManager.copyTheme(mTheme, other.mTheme);
1365
1366            mThemeResId = other.mThemeResId;
1367            mKey = other.mKey;
1368        }
1369
1370        /**
1371         * Return a TypedArray holding the values defined by
1372         * <var>Theme</var> which are listed in <var>attrs</var>.
1373         *
1374         * <p>Be sure to call {@link TypedArray#recycle() TypedArray.recycle()} when you are done
1375         * with the array.
1376         *
1377         * @param attrs The desired attributes.
1378         *
1379         * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1380         *
1381         * @return Returns a TypedArray holding an array of the attribute values.
1382         * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
1383         * when done with it.
1384         *
1385         * @see Resources#obtainAttributes
1386         * @see #obtainStyledAttributes(int, int[])
1387         * @see #obtainStyledAttributes(AttributeSet, int[], int, int)
1388         */
1389        public TypedArray obtainStyledAttributes(int[] attrs) {
1390            final int len = attrs.length;
1391            final TypedArray array = TypedArray.obtain(Resources.this, len);
1392            array.mTheme = this;
1393            AssetManager.applyStyle(mTheme, 0, 0, 0, attrs, array.mData, array.mIndices);
1394            return array;
1395        }
1396
1397        /**
1398         * Return a TypedArray holding the values defined by the style
1399         * resource <var>resid</var> which are listed in <var>attrs</var>.
1400         *
1401         * <p>Be sure to call {@link TypedArray#recycle() TypedArray.recycle()} when you are done
1402         * with the array.
1403         *
1404         * @param resid The desired style resource.
1405         * @param attrs The desired attributes in the style.
1406         *
1407         * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1408         *
1409         * @return Returns a TypedArray holding an array of the attribute values.
1410         * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
1411         * when done with it.
1412         *
1413         * @see Resources#obtainAttributes
1414         * @see #obtainStyledAttributes(int[])
1415         * @see #obtainStyledAttributes(AttributeSet, int[], int, int)
1416         */
1417        public TypedArray obtainStyledAttributes(int resid, int[] attrs) throws NotFoundException {
1418            final int len = attrs.length;
1419            final TypedArray array = TypedArray.obtain(Resources.this, len);
1420            array.mTheme = this;
1421            if (false) {
1422                int[] data = array.mData;
1423
1424                System.out.println("**********************************************************");
1425                System.out.println("**********************************************************");
1426                System.out.println("**********************************************************");
1427                System.out.println("Attributes:");
1428                String s = "  Attrs:";
1429                int i;
1430                for (i=0; i<attrs.length; i++) {
1431                    s = s + " 0x" + Integer.toHexString(attrs[i]);
1432                }
1433                System.out.println(s);
1434                s = "  Found:";
1435                TypedValue value = new TypedValue();
1436                for (i=0; i<attrs.length; i++) {
1437                    int d = i*AssetManager.STYLE_NUM_ENTRIES;
1438                    value.type = data[d+AssetManager.STYLE_TYPE];
1439                    value.data = data[d+AssetManager.STYLE_DATA];
1440                    value.assetCookie = data[d+AssetManager.STYLE_ASSET_COOKIE];
1441                    value.resourceId = data[d+AssetManager.STYLE_RESOURCE_ID];
1442                    s = s + " 0x" + Integer.toHexString(attrs[i])
1443                        + "=" + value;
1444                }
1445                System.out.println(s);
1446            }
1447            AssetManager.applyStyle(mTheme, 0, resid, 0, attrs, array.mData, array.mIndices);
1448            return array;
1449        }
1450
1451        /**
1452         * Return a TypedArray holding the attribute values in
1453         * <var>set</var>
1454         * that are listed in <var>attrs</var>.  In addition, if the given
1455         * AttributeSet specifies a style class (through the "style" attribute),
1456         * that style will be applied on top of the base attributes it defines.
1457         *
1458         * <p>Be sure to call {@link TypedArray#recycle() TypedArray.recycle()} when you are done
1459         * with the array.
1460         *
1461         * <p>When determining the final value of a particular attribute, there
1462         * are four inputs that come into play:</p>
1463         *
1464         * <ol>
1465         *     <li> Any attribute values in the given AttributeSet.
1466         *     <li> The style resource specified in the AttributeSet (named
1467         *     "style").
1468         *     <li> The default style specified by <var>defStyleAttr</var> and
1469         *     <var>defStyleRes</var>
1470         *     <li> The base values in this theme.
1471         * </ol>
1472         *
1473         * <p>Each of these inputs is considered in-order, with the first listed
1474         * taking precedence over the following ones.  In other words, if in the
1475         * AttributeSet you have supplied <code>&lt;Button
1476         * textColor="#ff000000"&gt;</code>, then the button's text will
1477         * <em>always</em> be black, regardless of what is specified in any of
1478         * the styles.
1479         *
1480         * @param set The base set of attribute values.  May be null.
1481         * @param attrs The desired attributes to be retrieved.
1482         * @param defStyleAttr An attribute in the current theme that contains a
1483         *                     reference to a style resource that supplies
1484         *                     defaults values for the TypedArray.  Can be
1485         *                     0 to not look for defaults.
1486         * @param defStyleRes A resource identifier of a style resource that
1487         *                    supplies default values for the TypedArray,
1488         *                    used only if defStyleAttr is 0 or can not be found
1489         *                    in the theme.  Can be 0 to not look for defaults.
1490         *
1491         * @return Returns a TypedArray holding an array of the attribute values.
1492         * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
1493         * when done with it.
1494         *
1495         * @see Resources#obtainAttributes
1496         * @see #obtainStyledAttributes(int[])
1497         * @see #obtainStyledAttributes(int, int[])
1498         */
1499        public TypedArray obtainStyledAttributes(AttributeSet set,
1500                int[] attrs, int defStyleAttr, int defStyleRes) {
1501            final int len = attrs.length;
1502            final TypedArray array = TypedArray.obtain(Resources.this, len);
1503
1504            // XXX note that for now we only work with compiled XML files.
1505            // To support generic XML files we will need to manually parse
1506            // out the attributes from the XML file (applying type information
1507            // contained in the resources and such).
1508            final XmlBlock.Parser parser = (XmlBlock.Parser)set;
1509            AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes,
1510                    parser != null ? parser.mParseState : 0, attrs, array.mData, array.mIndices);
1511
1512            array.mTheme = this;
1513            array.mXml = parser;
1514
1515            if (false) {
1516                int[] data = array.mData;
1517
1518                System.out.println("Attributes:");
1519                String s = "  Attrs:";
1520                int i;
1521                for (i=0; i<set.getAttributeCount(); i++) {
1522                    s = s + " " + set.getAttributeName(i);
1523                    int id = set.getAttributeNameResource(i);
1524                    if (id != 0) {
1525                        s = s + "(0x" + Integer.toHexString(id) + ")";
1526                    }
1527                    s = s + "=" + set.getAttributeValue(i);
1528                }
1529                System.out.println(s);
1530                s = "  Found:";
1531                TypedValue value = new TypedValue();
1532                for (i=0; i<attrs.length; i++) {
1533                    int d = i*AssetManager.STYLE_NUM_ENTRIES;
1534                    value.type = data[d+AssetManager.STYLE_TYPE];
1535                    value.data = data[d+AssetManager.STYLE_DATA];
1536                    value.assetCookie = data[d+AssetManager.STYLE_ASSET_COOKIE];
1537                    value.resourceId = data[d+AssetManager.STYLE_RESOURCE_ID];
1538                    s = s + " 0x" + Integer.toHexString(attrs[i])
1539                        + "=" + value;
1540                }
1541                System.out.println(s);
1542            }
1543
1544            return array;
1545        }
1546
1547        /**
1548         * Retrieve the values for a set of attributes in the Theme. The
1549         * contents of the typed array are ultimately filled in by
1550         * {@link Resources#getValue}.
1551         *
1552         * @param values The base set of attribute values, must be equal in
1553         *               length to {@code attrs}. All values must be of type
1554         *               {@link TypedValue#TYPE_ATTRIBUTE}.
1555         * @param attrs The desired attributes to be retrieved.
1556         * @return Returns a TypedArray holding an array of the attribute
1557         *         values. Be sure to call {@link TypedArray#recycle()}
1558         *         when done with it.
1559         * @hide
1560         */
1561        @NonNull
1562        public TypedArray resolveAttributes(@NonNull int[] values, @NonNull int[] attrs) {
1563            final int len = attrs.length;
1564            if (values == null || len != values.length) {
1565                throw new IllegalArgumentException(
1566                        "Base attribute values must the same length as attrs");
1567            }
1568
1569            final TypedArray array = TypedArray.obtain(Resources.this, len);
1570            AssetManager.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices);
1571            array.mTheme = this;
1572            array.mXml = null;
1573
1574            return array;
1575        }
1576
1577        /**
1578         * Retrieve the value of an attribute in the Theme.  The contents of
1579         * <var>outValue</var> are ultimately filled in by
1580         * {@link Resources#getValue}.
1581         *
1582         * @param resid The resource identifier of the desired theme
1583         *              attribute.
1584         * @param outValue Filled in with the ultimate resource value supplied
1585         *                 by the attribute.
1586         * @param resolveRefs If true, resource references will be walked; if
1587         *                    false, <var>outValue</var> may be a
1588         *                    TYPE_REFERENCE.  In either case, it will never
1589         *                    be a TYPE_ATTRIBUTE.
1590         *
1591         * @return boolean Returns true if the attribute was found and
1592         *         <var>outValue</var> is valid, else false.
1593         */
1594        public boolean resolveAttribute(int resid, TypedValue outValue, boolean resolveRefs) {
1595            boolean got = mAssets.getThemeValue(mTheme, resid, outValue, resolveRefs);
1596            if (false) {
1597                System.out.println(
1598                    "resolveAttribute #" + Integer.toHexString(resid)
1599                    + " got=" + got + ", type=0x" + Integer.toHexString(outValue.type)
1600                    + ", data=0x" + Integer.toHexString(outValue.data));
1601            }
1602            return got;
1603        }
1604
1605        /**
1606         * Gets all of the attribute ids associated with this {@link Theme}. For debugging only.
1607         *
1608         * @return The int array containing attribute ids associated with this {@link Theme}.
1609         * @hide
1610         */
1611        public int[] getAllAttributes() {
1612            return mAssets.getStyleAttributes(getAppliedStyleResId());
1613        }
1614
1615        /**
1616         * Returns the resources to which this theme belongs.
1617         *
1618         * @return Resources to which this theme belongs.
1619         */
1620        public Resources getResources() {
1621            return Resources.this;
1622        }
1623
1624        /**
1625         * Return a drawable object associated with a particular resource ID
1626         * and styled for the Theme.
1627         *
1628         * @param id The desired resource identifier, as generated by the aapt
1629         *           tool. This integer encodes the package, type, and resource
1630         *           entry. The value 0 is an invalid identifier.
1631         * @return Drawable An object that can be used to draw this resource.
1632         * @throws NotFoundException Throws NotFoundException if the given ID
1633         *         does not exist.
1634         */
1635        public Drawable getDrawable(int id) throws NotFoundException {
1636            return Resources.this.getDrawable(id, this);
1637        }
1638
1639        /**
1640         * Print contents of this theme out to the log.  For debugging only.
1641         *
1642         * @param priority The log priority to use.
1643         * @param tag The log tag to use.
1644         * @param prefix Text to prefix each line printed.
1645         */
1646        public void dump(int priority, String tag, String prefix) {
1647            AssetManager.dumpTheme(mTheme, priority, tag, prefix);
1648        }
1649
1650        @Override
1651        protected void finalize() throws Throwable {
1652            super.finalize();
1653            mAssets.releaseTheme(mTheme);
1654        }
1655
1656        /*package*/ Theme() {
1657            mAssets = Resources.this.mAssets;
1658            mTheme = mAssets.createTheme();
1659        }
1660
1661        @SuppressWarnings("hiding")
1662        private final AssetManager mAssets;
1663        private final long mTheme;
1664
1665        /** Resource identifier for the theme. */
1666        private int mThemeResId = 0;
1667
1668        /** Unique key for the series of styles applied to this theme. */
1669        private String mKey = "";
1670
1671        // Needed by layoutlib.
1672        /*package*/ long getNativeTheme() {
1673            return mTheme;
1674        }
1675
1676        /*package*/ int getAppliedStyleResId() {
1677            return mThemeResId;
1678        }
1679
1680        /*package*/ String getKey() {
1681            return mKey;
1682        }
1683
1684        private String getResourceNameFromHexString(String hexString) {
1685            return getResourceName(Integer.parseInt(hexString, 16));
1686        }
1687
1688        /**
1689         * Parses {@link #mKey} and returns a String array that holds pairs of adjacent Theme data:
1690         * resource name followed by whether or not it was forced, as specified by
1691         * {@link #applyStyle(int, boolean)}.
1692         *
1693         * @hide
1694         */
1695        @ViewDebug.ExportedProperty(category = "theme", hasAdjacentMapping = true)
1696        public String[] getTheme() {
1697            String[] themeData = mKey.split(" ");
1698            String[] themes = new String[themeData.length * 2];
1699            String theme;
1700            boolean forced;
1701
1702            for (int i = 0, j = themeData.length - 1; i < themes.length; i += 2, --j) {
1703                theme = themeData[j];
1704                forced = theme.endsWith("!");
1705                themes[i] = forced ?
1706                        getResourceNameFromHexString(theme.substring(0, theme.length() - 1)) :
1707                        getResourceNameFromHexString(theme);
1708                themes[i + 1] = forced ? "forced" : "not forced";
1709            }
1710            return themes;
1711        }
1712    }
1713
1714    /**
1715     * Generate a new Theme object for this set of Resources.  It initially
1716     * starts out empty.
1717     *
1718     * @return Theme The newly created Theme container.
1719     */
1720    public final Theme newTheme() {
1721        return new Theme();
1722    }
1723
1724    /**
1725     * Retrieve a set of basic attribute values from an AttributeSet, not
1726     * performing styling of them using a theme and/or style resources.
1727     *
1728     * @param set The current attribute values to retrieve.
1729     * @param attrs The specific attributes to be retrieved.
1730     * @return Returns a TypedArray holding an array of the attribute values.
1731     * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
1732     * when done with it.
1733     *
1734     * @see Theme#obtainStyledAttributes(AttributeSet, int[], int, int)
1735     */
1736    public TypedArray obtainAttributes(AttributeSet set, int[] attrs) {
1737        int len = attrs.length;
1738        TypedArray array = TypedArray.obtain(this, len);
1739
1740        // XXX note that for now we only work with compiled XML files.
1741        // To support generic XML files we will need to manually parse
1742        // out the attributes from the XML file (applying type information
1743        // contained in the resources and such).
1744        XmlBlock.Parser parser = (XmlBlock.Parser)set;
1745        mAssets.retrieveAttributes(parser.mParseState, attrs,
1746                array.mData, array.mIndices);
1747
1748        array.mXml = parser;
1749
1750        return array;
1751    }
1752
1753    /**
1754     * Store the newly updated configuration.
1755     */
1756    public void updateConfiguration(Configuration config,
1757            DisplayMetrics metrics) {
1758        updateConfiguration(config, metrics, null);
1759    }
1760
1761    /**
1762     * @hide
1763     */
1764    public void updateConfiguration(Configuration config,
1765            DisplayMetrics metrics, CompatibilityInfo compat) {
1766        synchronized (mAccessLock) {
1767            if (false) {
1768                Slog.i(TAG, "**** Updating config of " + this + ": old config is "
1769                        + mConfiguration + " old compat is " + mCompatibilityInfo);
1770                Slog.i(TAG, "**** Updating config of " + this + ": new config is "
1771                        + config + " new compat is " + compat);
1772            }
1773            if (compat != null) {
1774                mCompatibilityInfo = compat;
1775            }
1776            if (metrics != null) {
1777                mMetrics.setTo(metrics);
1778            }
1779            // NOTE: We should re-arrange this code to create a Display
1780            // with the CompatibilityInfo that is used everywhere we deal
1781            // with the display in relation to this app, rather than
1782            // doing the conversion here.  This impl should be okay because
1783            // we make sure to return a compatible display in the places
1784            // where there are public APIs to retrieve the display...  but
1785            // it would be cleaner and more maintainble to just be
1786            // consistently dealing with a compatible display everywhere in
1787            // the framework.
1788            mCompatibilityInfo.applyToDisplayMetrics(mMetrics);
1789
1790            int configChanges = calcConfigChanges(config);
1791            if (mConfiguration.locale == null) {
1792                mConfiguration.locale = Locale.getDefault();
1793                mConfiguration.setLayoutDirection(mConfiguration.locale);
1794            }
1795            if (mConfiguration.densityDpi != Configuration.DENSITY_DPI_UNDEFINED) {
1796                mMetrics.densityDpi = mConfiguration.densityDpi;
1797                mMetrics.density = mConfiguration.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
1798            }
1799            mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale;
1800
1801            String locale = null;
1802            if (mConfiguration.locale != null) {
1803                locale = adjustLanguageTag(mConfiguration.locale.toLanguageTag());
1804            }
1805            int width, height;
1806            if (mMetrics.widthPixels >= mMetrics.heightPixels) {
1807                width = mMetrics.widthPixels;
1808                height = mMetrics.heightPixels;
1809            } else {
1810                //noinspection SuspiciousNameCombination
1811                width = mMetrics.heightPixels;
1812                //noinspection SuspiciousNameCombination
1813                height = mMetrics.widthPixels;
1814            }
1815            int keyboardHidden = mConfiguration.keyboardHidden;
1816            if (keyboardHidden == Configuration.KEYBOARDHIDDEN_NO
1817                    && mConfiguration.hardKeyboardHidden
1818                            == Configuration.HARDKEYBOARDHIDDEN_YES) {
1819                keyboardHidden = Configuration.KEYBOARDHIDDEN_SOFT;
1820            }
1821            mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
1822                    locale, mConfiguration.orientation,
1823                    mConfiguration.touchscreen,
1824                    mConfiguration.densityDpi, mConfiguration.keyboard,
1825                    keyboardHidden, mConfiguration.navigation, width, height,
1826                    mConfiguration.smallestScreenWidthDp,
1827                    mConfiguration.screenWidthDp, mConfiguration.screenHeightDp,
1828                    mConfiguration.screenLayout, mConfiguration.uiMode,
1829                    Build.VERSION.RESOURCES_SDK_INT);
1830
1831            if (DEBUG_CONFIG) {
1832                Slog.i(TAG, "**** Updating config of " + this + ": final config is " + mConfiguration
1833                        + " final compat is " + mCompatibilityInfo);
1834            }
1835
1836            clearDrawableCachesLocked(mDrawableCache, configChanges);
1837            clearDrawableCachesLocked(mColorDrawableCache, configChanges);
1838            mAnimatorCache.onConfigurationChange(configChanges);
1839            mStateListAnimatorCache.onConfigurationChange(configChanges);
1840
1841            mColorStateListCache.clear();
1842
1843            flushLayoutCache();
1844        }
1845        synchronized (sSync) {
1846            if (mPluralRule != null) {
1847                mPluralRule = NativePluralRules.forLocale(config.locale);
1848            }
1849        }
1850    }
1851
1852    /**
1853     * Called by ConfigurationBoundResourceCacheTest via reflection.
1854     */
1855    private int calcConfigChanges(Configuration config) {
1856        int configChanges = 0xfffffff;
1857        if (config != null) {
1858            mTmpConfig.setTo(config);
1859            int density = config.densityDpi;
1860            if (density == Configuration.DENSITY_DPI_UNDEFINED) {
1861                density = mMetrics.noncompatDensityDpi;
1862            }
1863
1864            mCompatibilityInfo.applyToConfiguration(density, mTmpConfig);
1865
1866            if (mTmpConfig.locale == null) {
1867                mTmpConfig.locale = Locale.getDefault();
1868                mTmpConfig.setLayoutDirection(mTmpConfig.locale);
1869            }
1870            configChanges = mConfiguration.updateFrom(mTmpConfig);
1871            configChanges = ActivityInfo.activityInfoConfigToNative(configChanges);
1872        }
1873        return configChanges;
1874    }
1875
1876    private void clearDrawableCachesLocked(
1877            ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
1878            int configChanges) {
1879        final int N = caches.size();
1880        for (int i = 0; i < N; i++) {
1881            clearDrawableCacheLocked(caches.valueAt(i), configChanges);
1882        }
1883    }
1884
1885    private void clearDrawableCacheLocked(
1886            LongSparseArray<WeakReference<ConstantState>> cache, int configChanges) {
1887        if (DEBUG_CONFIG) {
1888            Log.d(TAG, "Cleaning up drawables config changes: 0x"
1889                    + Integer.toHexString(configChanges));
1890        }
1891        final int N = cache.size();
1892        for (int i = 0; i < N; i++) {
1893            final WeakReference<ConstantState> ref = cache.valueAt(i);
1894            if (ref != null) {
1895                final ConstantState cs = ref.get();
1896                if (cs != null) {
1897                    if (Configuration.needNewResources(
1898                            configChanges, cs.getChangingConfigurations())) {
1899                        if (DEBUG_CONFIG) {
1900                            Log.d(TAG, "FLUSHING #0x"
1901                                    + Long.toHexString(cache.keyAt(i))
1902                                    + " / " + cs + " with changes: 0x"
1903                                    + Integer.toHexString(cs.getChangingConfigurations()));
1904                        }
1905                        cache.setValueAt(i, null);
1906                    } else if (DEBUG_CONFIG) {
1907                        Log.d(TAG, "(Keeping #0x"
1908                                + Long.toHexString(cache.keyAt(i))
1909                                + " / " + cs + " with changes: 0x"
1910                                + Integer.toHexString(cs.getChangingConfigurations())
1911                                + ")");
1912                    }
1913                }
1914            }
1915        }
1916    }
1917
1918    /**
1919     * {@code Locale.toLanguageTag} will transform the obsolete (and deprecated)
1920     * language codes "in", "ji" and "iw" to "id", "yi" and "he" respectively.
1921     *
1922     * All released versions of android prior to "L" used the deprecated language
1923     * tags, so we will need to support them for backwards compatibility.
1924     *
1925     * Note that this conversion needs to take place *after* the call to
1926     * {@code toLanguageTag} because that will convert all the deprecated codes to
1927     * the new ones, even if they're set manually.
1928     */
1929    private static String adjustLanguageTag(String languageTag) {
1930        final int separator = languageTag.indexOf('-');
1931        final String language;
1932        final String remainder;
1933
1934        if (separator == -1) {
1935            language = languageTag;
1936            remainder = "";
1937        } else {
1938            language = languageTag.substring(0, separator);
1939            remainder = languageTag.substring(separator);
1940        }
1941
1942        return Locale.adjustLanguageCode(language) + remainder;
1943    }
1944
1945    /**
1946     * Update the system resources configuration if they have previously
1947     * been initialized.
1948     *
1949     * @hide
1950     */
1951    public static void updateSystemConfiguration(Configuration config, DisplayMetrics metrics,
1952            CompatibilityInfo compat) {
1953        if (mSystem != null) {
1954            mSystem.updateConfiguration(config, metrics, compat);
1955            //Log.i(TAG, "Updated system resources " + mSystem
1956            //        + ": " + mSystem.getConfiguration());
1957        }
1958    }
1959
1960    /**
1961     * Return the current display metrics that are in effect for this resource
1962     * object.  The returned object should be treated as read-only.
1963     *
1964     * @return The resource's current display metrics.
1965     */
1966    public DisplayMetrics getDisplayMetrics() {
1967        if (DEBUG_CONFIG) Slog.v(TAG, "Returning DisplayMetrics: " + mMetrics.widthPixels
1968                + "x" + mMetrics.heightPixels + " " + mMetrics.density);
1969        return mMetrics;
1970    }
1971
1972    /**
1973     * Return the current configuration that is in effect for this resource
1974     * object.  The returned object should be treated as read-only.
1975     *
1976     * @return The resource's current configuration.
1977     */
1978    public Configuration getConfiguration() {
1979        return mConfiguration;
1980    }
1981
1982    /**
1983     * Return the compatibility mode information for the application.
1984     * The returned object should be treated as read-only.
1985     *
1986     * @return compatibility info.
1987     * @hide
1988     */
1989    public CompatibilityInfo getCompatibilityInfo() {
1990        return mCompatibilityInfo;
1991    }
1992
1993    /**
1994     * This is just for testing.
1995     * @hide
1996     */
1997    public void setCompatibilityInfo(CompatibilityInfo ci) {
1998        if (ci != null) {
1999            mCompatibilityInfo = ci;
2000            updateConfiguration(mConfiguration, mMetrics);
2001        }
2002    }
2003
2004    /**
2005     * Return a resource identifier for the given resource name.  A fully
2006     * qualified resource name is of the form "package:type/entry".  The first
2007     * two components (package and type) are optional if defType and
2008     * defPackage, respectively, are specified here.
2009     *
2010     * <p>Note: use of this function is discouraged.  It is much more
2011     * efficient to retrieve resources by identifier than by name.
2012     *
2013     * @param name The name of the desired resource.
2014     * @param defType Optional default resource type to find, if "type/" is
2015     *                not included in the name.  Can be null to require an
2016     *                explicit type.
2017     * @param defPackage Optional default package to find, if "package:" is
2018     *                   not included in the name.  Can be null to require an
2019     *                   explicit package.
2020     *
2021     * @return int The associated resource identifier.  Returns 0 if no such
2022     *         resource was found.  (0 is not a valid resource ID.)
2023     */
2024    public int getIdentifier(String name, String defType, String defPackage) {
2025        if (name == null) {
2026            throw new NullPointerException("name is null");
2027        }
2028        try {
2029            return Integer.parseInt(name);
2030        } catch (Exception e) {
2031            // Ignore
2032        }
2033        return mAssets.getResourceIdentifier(name, defType, defPackage);
2034    }
2035
2036    /**
2037     * Return true if given resource identifier includes a package.
2038     *
2039     * @hide
2040     */
2041    public static boolean resourceHasPackage(int resid) {
2042        return (resid >>> 24) != 0;
2043    }
2044
2045    /**
2046     * Return the full name for a given resource identifier.  This name is
2047     * a single string of the form "package:type/entry".
2048     *
2049     * @param resid The resource identifier whose name is to be retrieved.
2050     *
2051     * @return A string holding the name of the resource.
2052     *
2053     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
2054     *
2055     * @see #getResourcePackageName
2056     * @see #getResourceTypeName
2057     * @see #getResourceEntryName
2058     */
2059    public String getResourceName(int resid) throws NotFoundException {
2060        String str = mAssets.getResourceName(resid);
2061        if (str != null) return str;
2062        throw new NotFoundException("Unable to find resource ID #0x"
2063                + Integer.toHexString(resid));
2064    }
2065
2066    /**
2067     * Return the package name for a given resource identifier.
2068     *
2069     * @param resid The resource identifier whose package name is to be
2070     * retrieved.
2071     *
2072     * @return A string holding the package name of the resource.
2073     *
2074     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
2075     *
2076     * @see #getResourceName
2077     */
2078    public String getResourcePackageName(int resid) throws NotFoundException {
2079        String str = mAssets.getResourcePackageName(resid);
2080        if (str != null) return str;
2081        throw new NotFoundException("Unable to find resource ID #0x"
2082                + Integer.toHexString(resid));
2083    }
2084
2085    /**
2086     * Return the type name for a given resource identifier.
2087     *
2088     * @param resid The resource identifier whose type name is to be
2089     * retrieved.
2090     *
2091     * @return A string holding the type name of the resource.
2092     *
2093     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
2094     *
2095     * @see #getResourceName
2096     */
2097    public String getResourceTypeName(int resid) throws NotFoundException {
2098        String str = mAssets.getResourceTypeName(resid);
2099        if (str != null) return str;
2100        throw new NotFoundException("Unable to find resource ID #0x"
2101                + Integer.toHexString(resid));
2102    }
2103
2104    /**
2105     * Return the entry name for a given resource identifier.
2106     *
2107     * @param resid The resource identifier whose entry name is to be
2108     * retrieved.
2109     *
2110     * @return A string holding the entry name of the resource.
2111     *
2112     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
2113     *
2114     * @see #getResourceName
2115     */
2116    public String getResourceEntryName(int resid) throws NotFoundException {
2117        String str = mAssets.getResourceEntryName(resid);
2118        if (str != null) return str;
2119        throw new NotFoundException("Unable to find resource ID #0x"
2120                + Integer.toHexString(resid));
2121    }
2122
2123    /**
2124     * Parse a series of {@link android.R.styleable#Extra &lt;extra&gt;} tags from
2125     * an XML file.  You call this when you are at the parent tag of the
2126     * extra tags, and it will return once all of the child tags have been parsed.
2127     * This will call {@link #parseBundleExtra} for each extra tag encountered.
2128     *
2129     * @param parser The parser from which to retrieve the extras.
2130     * @param outBundle A Bundle in which to place all parsed extras.
2131     * @throws XmlPullParserException
2132     * @throws IOException
2133     */
2134    public void parseBundleExtras(XmlResourceParser parser, Bundle outBundle)
2135            throws XmlPullParserException, IOException {
2136        int outerDepth = parser.getDepth();
2137        int type;
2138        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
2139               && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
2140            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
2141                continue;
2142            }
2143
2144            String nodeName = parser.getName();
2145            if (nodeName.equals("extra")) {
2146                parseBundleExtra("extra", parser, outBundle);
2147                XmlUtils.skipCurrentTag(parser);
2148
2149            } else {
2150                XmlUtils.skipCurrentTag(parser);
2151            }
2152        }
2153    }
2154
2155    /**
2156     * Parse a name/value pair out of an XML tag holding that data.  The
2157     * AttributeSet must be holding the data defined by
2158     * {@link android.R.styleable#Extra}.  The following value types are supported:
2159     * <ul>
2160     * <li> {@link TypedValue#TYPE_STRING}:
2161     * {@link Bundle#putCharSequence Bundle.putCharSequence()}
2162     * <li> {@link TypedValue#TYPE_INT_BOOLEAN}:
2163     * {@link Bundle#putCharSequence Bundle.putBoolean()}
2164     * <li> {@link TypedValue#TYPE_FIRST_INT}-{@link TypedValue#TYPE_LAST_INT}:
2165     * {@link Bundle#putCharSequence Bundle.putBoolean()}
2166     * <li> {@link TypedValue#TYPE_FLOAT}:
2167     * {@link Bundle#putCharSequence Bundle.putFloat()}
2168     * </ul>
2169     *
2170     * @param tagName The name of the tag these attributes come from; this is
2171     * only used for reporting error messages.
2172     * @param attrs The attributes from which to retrieve the name/value pair.
2173     * @param outBundle The Bundle in which to place the parsed value.
2174     * @throws XmlPullParserException If the attributes are not valid.
2175     */
2176    public void parseBundleExtra(String tagName, AttributeSet attrs,
2177            Bundle outBundle) throws XmlPullParserException {
2178        TypedArray sa = obtainAttributes(attrs,
2179                com.android.internal.R.styleable.Extra);
2180
2181        String name = sa.getString(
2182                com.android.internal.R.styleable.Extra_name);
2183        if (name == null) {
2184            sa.recycle();
2185            throw new XmlPullParserException("<" + tagName
2186                    + "> requires an android:name attribute at "
2187                    + attrs.getPositionDescription());
2188        }
2189
2190        TypedValue v = sa.peekValue(
2191                com.android.internal.R.styleable.Extra_value);
2192        if (v != null) {
2193            if (v.type == TypedValue.TYPE_STRING) {
2194                CharSequence cs = v.coerceToString();
2195                outBundle.putCharSequence(name, cs);
2196            } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) {
2197                outBundle.putBoolean(name, v.data != 0);
2198            } else if (v.type >= TypedValue.TYPE_FIRST_INT
2199                    && v.type <= TypedValue.TYPE_LAST_INT) {
2200                outBundle.putInt(name, v.data);
2201            } else if (v.type == TypedValue.TYPE_FLOAT) {
2202                outBundle.putFloat(name, v.getFloat());
2203            } else {
2204                sa.recycle();
2205                throw new XmlPullParserException("<" + tagName
2206                        + "> only supports string, integer, float, color, and boolean at "
2207                        + attrs.getPositionDescription());
2208            }
2209        } else {
2210            sa.recycle();
2211            throw new XmlPullParserException("<" + tagName
2212                    + "> requires an android:value or android:resource attribute at "
2213                    + attrs.getPositionDescription());
2214        }
2215
2216        sa.recycle();
2217    }
2218
2219    /**
2220     * Retrieve underlying AssetManager storage for these resources.
2221     */
2222    public final AssetManager getAssets() {
2223        return mAssets;
2224    }
2225
2226    /**
2227     * Call this to remove all cached loaded layout resources from the
2228     * Resources object.  Only intended for use with performance testing
2229     * tools.
2230     */
2231    public final void flushLayoutCache() {
2232        synchronized (mCachedXmlBlockIds) {
2233            // First see if this block is in our cache.
2234            final int num = mCachedXmlBlockIds.length;
2235            for (int i=0; i<num; i++) {
2236                mCachedXmlBlockIds[i] = -0;
2237                XmlBlock oldBlock = mCachedXmlBlocks[i];
2238                if (oldBlock != null) {
2239                    oldBlock.close();
2240                }
2241                mCachedXmlBlocks[i] = null;
2242            }
2243        }
2244    }
2245
2246    /**
2247     * Start preloading of resource data using this Resources object.  Only
2248     * for use by the zygote process for loading common system resources.
2249     * {@hide}
2250     */
2251    public final void startPreloading() {
2252        synchronized (sSync) {
2253            if (sPreloaded) {
2254                throw new IllegalStateException("Resources already preloaded");
2255            }
2256            sPreloaded = true;
2257            mPreloading = true;
2258            sPreloadedDensity = DisplayMetrics.DENSITY_DEVICE;
2259            mConfiguration.densityDpi = sPreloadedDensity;
2260            updateConfiguration(null, null);
2261        }
2262    }
2263
2264    /**
2265     * Called by zygote when it is done preloading resources, to change back
2266     * to normal Resources operation.
2267     */
2268    public final void finishPreloading() {
2269        if (mPreloading) {
2270            mPreloading = false;
2271            flushLayoutCache();
2272        }
2273    }
2274
2275    /**
2276     * @hide
2277     */
2278    public LongSparseArray<ConstantState> getPreloadedDrawables() {
2279        return sPreloadedDrawables[0];
2280    }
2281
2282    private boolean verifyPreloadConfig(int changingConfigurations, int allowVarying,
2283            int resourceId, String name) {
2284        // We allow preloading of resources even if they vary by font scale (which
2285        // doesn't impact resource selection) or density (which we handle specially by
2286        // simply turning off all preloading), as well as any other configs specified
2287        // by the caller.
2288        if (((changingConfigurations&~(ActivityInfo.CONFIG_FONT_SCALE |
2289                ActivityInfo.CONFIG_DENSITY)) & ~allowVarying) != 0) {
2290            String resName;
2291            try {
2292                resName = getResourceName(resourceId);
2293            } catch (NotFoundException e) {
2294                resName = "?";
2295            }
2296            // This should never happen in production, so we should log a
2297            // warning even if we're not debugging.
2298            Log.w(TAG, "Preloaded " + name + " resource #0x"
2299                    + Integer.toHexString(resourceId)
2300                    + " (" + resName + ") that varies with configuration!!");
2301            return false;
2302        }
2303        if (TRACE_FOR_PRELOAD) {
2304            String resName;
2305            try {
2306                resName = getResourceName(resourceId);
2307            } catch (NotFoundException e) {
2308                resName = "?";
2309            }
2310            Log.w(TAG, "Preloading " + name + " resource #0x"
2311                    + Integer.toHexString(resourceId)
2312                    + " (" + resName + ")");
2313        }
2314        return true;
2315    }
2316
2317    /*package*/ Drawable loadDrawable(TypedValue value, int id, Theme theme) throws NotFoundException {
2318        if (TRACE_FOR_PRELOAD) {
2319            // Log only framework resources
2320            if ((id >>> 24) == 0x1) {
2321                final String name = getResourceName(id);
2322                if (name != null) {
2323                    Log.d("PreloadDrawable", name);
2324                }
2325            }
2326        }
2327
2328        final boolean isColorDrawable;
2329        final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches;
2330        final long key;
2331        if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
2332                && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
2333            isColorDrawable = true;
2334            caches = mColorDrawableCache;
2335            key = value.data;
2336        } else {
2337            isColorDrawable = false;
2338            caches = mDrawableCache;
2339            key = (((long) value.assetCookie) << 32) | value.data;
2340        }
2341
2342        // First, check whether we have a cached version of this drawable
2343        // that was inflated against the specified theme.
2344        if (!mPreloading) {
2345            final Drawable cachedDrawable = getCachedDrawable(caches, key, theme);
2346            if (cachedDrawable != null) {
2347                return cachedDrawable;
2348            }
2349        }
2350
2351        // Next, check preloaded drawables. These are unthemed but may have
2352        // themeable attributes.
2353        final ConstantState cs;
2354        if (isColorDrawable) {
2355            cs = sPreloadedColorDrawables.get(key);
2356        } else {
2357            cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key);
2358        }
2359
2360        final Drawable dr;
2361        if (cs != null) {
2362            final Drawable clonedDr = cs.newDrawable(this);
2363            if (theme != null) {
2364                dr = clonedDr.mutate();
2365                dr.applyTheme(theme);
2366                dr.clearMutated();
2367            } else {
2368                dr = clonedDr;
2369            }
2370        } else if (isColorDrawable) {
2371            dr = new ColorDrawable(value.data);
2372        } else {
2373            dr = loadDrawableForCookie(value, id, theme);
2374        }
2375
2376        // If we were able to obtain a drawable, store it in the appropriate
2377        // cache (either preload or themed).
2378        if (dr != null) {
2379            dr.setChangingConfigurations(value.changingConfigurations);
2380            cacheDrawable(value, theme, isColorDrawable, caches, key, dr);
2381        }
2382
2383        return dr;
2384    }
2385
2386    private void cacheDrawable(TypedValue value, Theme theme, boolean isColorDrawable,
2387            ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
2388            long key, Drawable dr) {
2389        final ConstantState cs = dr.getConstantState();
2390        if (cs == null) {
2391            return;
2392        }
2393
2394        if (mPreloading) {
2395            // Preloaded drawables never have a theme, but may be themeable.
2396            final int changingConfigs = cs.getChangingConfigurations();
2397            if (isColorDrawable) {
2398                if (verifyPreloadConfig(changingConfigs, 0, value.resourceId, "drawable")) {
2399                    sPreloadedColorDrawables.put(key, cs);
2400                }
2401            } else {
2402                if (verifyPreloadConfig(
2403                        changingConfigs, LAYOUT_DIR_CONFIG, value.resourceId, "drawable")) {
2404                    if ((changingConfigs & LAYOUT_DIR_CONFIG) == 0) {
2405                        // If this resource does not vary based on layout direction,
2406                        // we can put it in all of the preload maps.
2407                        sPreloadedDrawables[0].put(key, cs);
2408                        sPreloadedDrawables[1].put(key, cs);
2409                    } else {
2410                        // Otherwise, only in the layout dir we loaded it for.
2411                        sPreloadedDrawables[mConfiguration.getLayoutDirection()].put(key, cs);
2412                    }
2413                }
2414            }
2415        } else {
2416            synchronized (mAccessLock) {
2417                final String themeKey = theme == null ? "" : theme.mKey;
2418                LongSparseArray<WeakReference<ConstantState>> themedCache = caches.get(themeKey);
2419                if (themedCache == null) {
2420                    themedCache = new LongSparseArray<WeakReference<ConstantState>>(1);
2421                    caches.put(themeKey, themedCache);
2422                }
2423                themedCache.put(key, new WeakReference<ConstantState>(cs));
2424            }
2425        }
2426    }
2427
2428    /**
2429     * Loads a drawable from XML or resources stream.
2430     */
2431    private Drawable loadDrawableForCookie(TypedValue value, int id, Theme theme) {
2432        if (value.string == null) {
2433            throw new NotFoundException("Resource \"" + getResourceName(id) + "\" ("
2434                    + Integer.toHexString(id) + ")  is not a Drawable (color or path): " + value);
2435        }
2436
2437        final String file = value.string.toString();
2438
2439        if (TRACE_FOR_MISS_PRELOAD) {
2440            // Log only framework resources
2441            if ((id >>> 24) == 0x1) {
2442                final String name = getResourceName(id);
2443                if (name != null) {
2444                    Log.d(TAG, "Loading framework drawable #" + Integer.toHexString(id)
2445                            + ": " + name + " at " + file);
2446                }
2447            }
2448        }
2449
2450        if (DEBUG_LOAD) {
2451            Log.v(TAG, "Loading drawable for cookie " + value.assetCookie + ": " + file);
2452        }
2453
2454        final Drawable dr;
2455
2456        Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
2457        try {
2458            if (file.endsWith(".xml")) {
2459                final XmlResourceParser rp = loadXmlResourceParser(
2460                        file, id, value.assetCookie, "drawable");
2461                dr = Drawable.createFromXml(this, rp, theme);
2462                rp.close();
2463            } else {
2464                final InputStream is = mAssets.openNonAsset(
2465                        value.assetCookie, file, AssetManager.ACCESS_STREAMING);
2466                dr = Drawable.createFromResourceStream(this, value, is, file, null);
2467                is.close();
2468            }
2469        } catch (Exception e) {
2470            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
2471            final NotFoundException rnf = new NotFoundException(
2472                    "File " + file + " from drawable resource ID #0x" + Integer.toHexString(id));
2473            rnf.initCause(e);
2474            throw rnf;
2475        }
2476        Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
2477
2478        return dr;
2479    }
2480
2481    private Drawable getCachedDrawable(
2482            ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
2483            long key, Theme theme) {
2484        synchronized (mAccessLock) {
2485            final String themeKey = theme != null ? theme.mKey : "";
2486            final LongSparseArray<WeakReference<ConstantState>> themedCache = caches.get(themeKey);
2487            if (themedCache != null) {
2488                final Drawable themedDrawable = getCachedDrawableLocked(themedCache, key);
2489                if (themedDrawable != null) {
2490                    return themedDrawable;
2491                }
2492            }
2493
2494            // No cached drawable, we'll need to create a new one.
2495            return null;
2496        }
2497    }
2498
2499    private ConstantState getConstantStateLocked(
2500            LongSparseArray<WeakReference<ConstantState>> drawableCache, long key) {
2501        final WeakReference<ConstantState> wr = drawableCache.get(key);
2502        if (wr != null) {   // we have the key
2503            final ConstantState entry = wr.get();
2504            if (entry != null) {
2505                //Log.i(TAG, "Returning cached drawable @ #" +
2506                //        Integer.toHexString(((Integer)key).intValue())
2507                //        + " in " + this + ": " + entry);
2508                return entry;
2509            } else {  // our entry has been purged
2510                drawableCache.delete(key);
2511            }
2512        }
2513        return null;
2514    }
2515
2516    private Drawable getCachedDrawableLocked(
2517            LongSparseArray<WeakReference<ConstantState>> drawableCache, long key) {
2518        final ConstantState entry = getConstantStateLocked(drawableCache, key);
2519        if (entry != null) {
2520            return entry.newDrawable(this);
2521        }
2522        return null;
2523    }
2524
2525    /*package*/ ColorStateList loadColorStateList(TypedValue value, int id)
2526            throws NotFoundException {
2527        if (TRACE_FOR_PRELOAD) {
2528            // Log only framework resources
2529            if ((id >>> 24) == 0x1) {
2530                final String name = getResourceName(id);
2531                if (name != null) android.util.Log.d("PreloadColorStateList", name);
2532            }
2533        }
2534
2535        final long key = (((long) value.assetCookie) << 32) | value.data;
2536
2537        ColorStateList csl;
2538
2539        if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
2540                value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
2541
2542            csl = sPreloadedColorStateLists.get(key);
2543            if (csl != null) {
2544                return csl;
2545            }
2546
2547            csl = ColorStateList.valueOf(value.data);
2548            if (mPreloading) {
2549                if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId,
2550                        "color")) {
2551                    sPreloadedColorStateLists.put(key, csl);
2552                }
2553            }
2554
2555            return csl;
2556        }
2557
2558        csl = getCachedColorStateList(key);
2559        if (csl != null) {
2560            return csl;
2561        }
2562
2563        csl = sPreloadedColorStateLists.get(key);
2564        if (csl != null) {
2565            return csl;
2566        }
2567
2568        if (value.string == null) {
2569            throw new NotFoundException(
2570                    "Resource is not a ColorStateList (color or path): " + value);
2571        }
2572
2573        final String file = value.string.toString();
2574
2575        if (file.endsWith(".xml")) {
2576            Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
2577            try {
2578                final XmlResourceParser rp = loadXmlResourceParser(
2579                        file, id, value.assetCookie, "colorstatelist");
2580                csl = ColorStateList.createFromXml(this, rp);
2581                rp.close();
2582            } catch (Exception e) {
2583                Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
2584                NotFoundException rnf = new NotFoundException(
2585                    "File " + file + " from color state list resource ID #0x"
2586                    + Integer.toHexString(id));
2587                rnf.initCause(e);
2588                throw rnf;
2589            }
2590            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
2591        } else {
2592            throw new NotFoundException(
2593                    "File " + file + " from drawable resource ID #0x"
2594                    + Integer.toHexString(id) + ": .xml extension required");
2595        }
2596
2597        if (csl != null) {
2598            if (mPreloading) {
2599                if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId,
2600                        "color")) {
2601                    sPreloadedColorStateLists.put(key, csl);
2602                }
2603            } else {
2604                synchronized (mAccessLock) {
2605                    //Log.i(TAG, "Saving cached color state list @ #" +
2606                    //        Integer.toHexString(key.intValue())
2607                    //        + " in " + this + ": " + csl);
2608                    mColorStateListCache.put(key, new WeakReference<ColorStateList>(csl));
2609                }
2610            }
2611        }
2612
2613        return csl;
2614    }
2615
2616    private ColorStateList getCachedColorStateList(long key) {
2617        synchronized (mAccessLock) {
2618            WeakReference<ColorStateList> wr = mColorStateListCache.get(key);
2619            if (wr != null) {   // we have the key
2620                ColorStateList entry = wr.get();
2621                if (entry != null) {
2622                    //Log.i(TAG, "Returning cached color state list @ #" +
2623                    //        Integer.toHexString(((Integer)key).intValue())
2624                    //        + " in " + this + ": " + entry);
2625                    return entry;
2626                } else {  // our entry has been purged
2627                    mColorStateListCache.delete(key);
2628                }
2629            }
2630        }
2631        return null;
2632    }
2633
2634    /*package*/ XmlResourceParser loadXmlResourceParser(int id, String type)
2635            throws NotFoundException {
2636        synchronized (mAccessLock) {
2637            TypedValue value = mTmpValue;
2638            if (value == null) {
2639                mTmpValue = value = new TypedValue();
2640            }
2641            getValue(id, value, true);
2642            if (value.type == TypedValue.TYPE_STRING) {
2643                return loadXmlResourceParser(value.string.toString(), id,
2644                        value.assetCookie, type);
2645            }
2646            throw new NotFoundException(
2647                    "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
2648                    + Integer.toHexString(value.type) + " is not valid");
2649        }
2650    }
2651
2652    /*package*/ XmlResourceParser loadXmlResourceParser(String file, int id,
2653            int assetCookie, String type) throws NotFoundException {
2654        if (id != 0) {
2655            try {
2656                // These may be compiled...
2657                synchronized (mCachedXmlBlockIds) {
2658                    // First see if this block is in our cache.
2659                    final int num = mCachedXmlBlockIds.length;
2660                    for (int i=0; i<num; i++) {
2661                        if (mCachedXmlBlockIds[i] == id) {
2662                            //System.out.println("**** REUSING XML BLOCK!  id="
2663                            //                   + id + ", index=" + i);
2664                            return mCachedXmlBlocks[i].newParser();
2665                        }
2666                    }
2667
2668                    // Not in the cache, create a new block and put it at
2669                    // the next slot in the cache.
2670                    XmlBlock block = mAssets.openXmlBlockAsset(
2671                            assetCookie, file);
2672                    if (block != null) {
2673                        int pos = mLastCachedXmlBlockIndex+1;
2674                        if (pos >= num) pos = 0;
2675                        mLastCachedXmlBlockIndex = pos;
2676                        XmlBlock oldBlock = mCachedXmlBlocks[pos];
2677                        if (oldBlock != null) {
2678                            oldBlock.close();
2679                        }
2680                        mCachedXmlBlockIds[pos] = id;
2681                        mCachedXmlBlocks[pos] = block;
2682                        //System.out.println("**** CACHING NEW XML BLOCK!  id="
2683                        //                   + id + ", index=" + pos);
2684                        return block.newParser();
2685                    }
2686                }
2687            } catch (Exception e) {
2688                NotFoundException rnf = new NotFoundException(
2689                        "File " + file + " from xml type " + type + " resource ID #0x"
2690                        + Integer.toHexString(id));
2691                rnf.initCause(e);
2692                throw rnf;
2693            }
2694        }
2695
2696        throw new NotFoundException(
2697                "File " + file + " from xml type " + type + " resource ID #0x"
2698                + Integer.toHexString(id));
2699    }
2700
2701    /*package*/ void recycleCachedStyledAttributes(TypedArray attrs) {
2702        synchronized (mAccessLock) {
2703            final TypedArray cached = mCachedStyledAttributes;
2704            if (cached == null || cached.mData.length < attrs.mData.length) {
2705                mCachedStyledAttributes = attrs;
2706            }
2707        }
2708    }
2709
2710    private Resources() {
2711        mAssets = AssetManager.getSystem();
2712        // NOTE: Intentionally leaving this uninitialized (all values set
2713        // to zero), so that anyone who tries to do something that requires
2714        // metrics will get a very wrong value.
2715        mConfiguration.setToDefaults();
2716        mMetrics.setToDefaults();
2717        updateConfiguration(null, null);
2718        mAssets.ensureStringBlocks();
2719    }
2720}
2721