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