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