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