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