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