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