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