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