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