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