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