Resources.java revision 50707cc8206f0d257ba83de2f1f99c3eaa13a4bd
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        }
796        ColorStateList csl = loadColorStateList(value, id);
797        synchronized (mAccessLock) {
798            if (mTmpValue == null) {
799                mTmpValue = value;
800            }
801        }
802        return csl.getDefaultColor();
803    }
804
805    /**
806     * Return a color state list associated with a particular resource ID.  The
807     * resource may contain either a single raw color value, or a complex
808     * {@link android.content.res.ColorStateList} holding multiple possible colors.
809     *
810     * @param id The desired resource identifier of a {@link ColorStateList},
811     *        as generated by the aapt tool. This integer encodes the package, type, and resource
812     *        entry. The value 0 is an invalid identifier.
813     *
814     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
815     *
816     * @return Returns a ColorStateList object containing either a single
817     * solid color or multiple colors that can be selected based on a state.
818     */
819    public ColorStateList getColorStateList(int id) throws NotFoundException {
820        TypedValue value;
821        synchronized (mAccessLock) {
822            value = mTmpValue;
823            if (value == null) {
824                value = new TypedValue();
825            } else {
826                mTmpValue = null;
827            }
828            getValue(id, value, true);
829        }
830        ColorStateList res = loadColorStateList(value, id);
831        synchronized (mAccessLock) {
832            if (mTmpValue == null) {
833                mTmpValue = value;
834            }
835        }
836        return res;
837    }
838
839    /**
840     * Return a boolean associated with a particular resource ID.  This can be
841     * used with any integral resource value, and will return true if it is
842     * non-zero.
843     *
844     * @param id The desired resource identifier, as generated by the aapt
845     *           tool. This integer encodes the package, type, and resource
846     *           entry. The value 0 is an invalid identifier.
847     *
848     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
849     *
850     * @return Returns the boolean value contained in the resource.
851     */
852    public boolean getBoolean(int id) throws NotFoundException {
853        synchronized (mAccessLock) {
854            TypedValue value = mTmpValue;
855            if (value == null) {
856                mTmpValue = value = new TypedValue();
857            }
858            getValue(id, value, true);
859            if (value.type >= TypedValue.TYPE_FIRST_INT
860                && value.type <= TypedValue.TYPE_LAST_INT) {
861                return value.data != 0;
862            }
863            throw new NotFoundException(
864                "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
865                + Integer.toHexString(value.type) + " is not valid");
866        }
867    }
868
869    /**
870     * Return an integer associated with a particular resource ID.
871     *
872     * @param id The desired resource identifier, as generated by the aapt
873     *           tool. This integer encodes the package, type, and resource
874     *           entry. The value 0 is an invalid identifier.
875     *
876     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
877     *
878     * @return Returns the integer value contained in the resource.
879     */
880    public int getInteger(int id) throws NotFoundException {
881        synchronized (mAccessLock) {
882            TypedValue value = mTmpValue;
883            if (value == null) {
884                mTmpValue = value = new TypedValue();
885            }
886            getValue(id, value, true);
887            if (value.type >= TypedValue.TYPE_FIRST_INT
888                && value.type <= TypedValue.TYPE_LAST_INT) {
889                return value.data;
890            }
891            throw new NotFoundException(
892                "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
893                + Integer.toHexString(value.type) + " is not valid");
894        }
895    }
896
897    /**
898     * Return an XmlResourceParser through which you can read a view layout
899     * description for the given resource ID.  This parser has limited
900     * functionality -- in particular, you can't change its input, and only
901     * the high-level events are available.
902     *
903     * <p>This function is really a simple wrapper for calling
904     * {@link #getXml} with a layout resource.
905     *
906     * @param id The desired resource identifier, as generated by the aapt
907     *           tool. This integer encodes the package, type, and resource
908     *           entry. The value 0 is an invalid identifier.
909     *
910     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
911     *
912     * @return A new parser object through which you can read
913     *         the XML data.
914     *
915     * @see #getXml
916     */
917    public XmlResourceParser getLayout(int id) throws NotFoundException {
918        return loadXmlResourceParser(id, "layout");
919    }
920
921    /**
922     * Return an XmlResourceParser through which you can read an animation
923     * description for the given resource ID.  This parser has limited
924     * functionality -- in particular, you can't change its input, and only
925     * the high-level events are available.
926     *
927     * <p>This function is really a simple wrapper for calling
928     * {@link #getXml} with an animation resource.
929     *
930     * @param id The desired resource identifier, as generated by the aapt
931     *           tool. This integer encodes the package, type, and resource
932     *           entry. The value 0 is an invalid identifier.
933     *
934     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
935     *
936     * @return A new parser object through which you can read
937     *         the XML data.
938     *
939     * @see #getXml
940     */
941    public XmlResourceParser getAnimation(int id) throws NotFoundException {
942        return loadXmlResourceParser(id, "anim");
943    }
944
945    /**
946     * Return an XmlResourceParser through which you can read a generic XML
947     * resource for the given resource ID.
948     *
949     * <p>The XmlPullParser implementation returned here has some limited
950     * functionality.  In particular, you can't change its input, and only
951     * high-level parsing events are available (since the document was
952     * pre-parsed for you at build time, which involved merging text and
953     * stripping comments).
954     *
955     * @param id The desired resource identifier, as generated by the aapt
956     *           tool. This integer encodes the package, type, and resource
957     *           entry. The value 0 is an invalid identifier.
958     *
959     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
960     *
961     * @return A new parser object through which you can read
962     *         the XML data.
963     *
964     * @see android.util.AttributeSet
965     */
966    public XmlResourceParser getXml(int id) throws NotFoundException {
967        return loadXmlResourceParser(id, "xml");
968    }
969
970    /**
971     * Open a data stream for reading a raw resource.  This can only be used
972     * with resources whose value is the name of an asset files -- that is, it can be
973     * used to open drawable, sound, and raw resources; it will fail on string
974     * and color resources.
975     *
976     * @param id The resource identifier to open, as generated by the appt
977     *           tool.
978     *
979     * @return InputStream Access to the resource data.
980     *
981     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
982     *
983     */
984    public InputStream openRawResource(int id) throws NotFoundException {
985        TypedValue value;
986        synchronized (mAccessLock) {
987            value = mTmpValue;
988            if (value == null) {
989                value = new TypedValue();
990            } else {
991                mTmpValue = null;
992            }
993        }
994        InputStream res = openRawResource(id, value);
995        synchronized (mAccessLock) {
996            if (mTmpValue == null) {
997                mTmpValue = value;
998            }
999        }
1000        return res;
1001    }
1002
1003    /**
1004     * Open a data stream for reading a raw resource.  This can only be used
1005     * with resources whose value is the name of an asset file -- that is, it can be
1006     * used to open drawable, sound, and raw resources; it will fail on string
1007     * and color resources.
1008     *
1009     * @param id The resource identifier to open, as generated by the appt tool.
1010     * @param value The TypedValue object to hold the resource information.
1011     *
1012     * @return InputStream Access to the resource data.
1013     *
1014     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1015     */
1016    public InputStream openRawResource(int id, TypedValue value) throws NotFoundException {
1017        getValue(id, value, true);
1018
1019        try {
1020            return mAssets.openNonAsset(value.assetCookie, value.string.toString(),
1021                    AssetManager.ACCESS_STREAMING);
1022        } catch (Exception e) {
1023            NotFoundException rnf = new NotFoundException("File " + value.string.toString() +
1024                    " from drawable resource ID #0x" + Integer.toHexString(id));
1025            rnf.initCause(e);
1026            throw rnf;
1027        }
1028    }
1029
1030    /**
1031     * Open a file descriptor for reading a raw resource.  This can only be used
1032     * with resources whose value is the name of an asset files -- that is, it can be
1033     * used to open drawable, sound, and raw resources; it will fail on string
1034     * and color resources.
1035     *
1036     * <p>This function only works for resources that are stored in the package
1037     * as uncompressed data, which typically includes things like mp3 files
1038     * and png images.
1039     *
1040     * @param id The resource identifier to open, as generated by the appt
1041     *           tool.
1042     *
1043     * @return AssetFileDescriptor A new file descriptor you can use to read
1044     * the resource.  This includes the file descriptor itself, as well as the
1045     * offset and length of data where the resource appears in the file.  A
1046     * null is returned if the file exists but is compressed.
1047     *
1048     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1049     *
1050     */
1051    public AssetFileDescriptor openRawResourceFd(int id) throws NotFoundException {
1052        TypedValue value;
1053        synchronized (mAccessLock) {
1054            value = mTmpValue;
1055            if (value == null) {
1056                value = new TypedValue();
1057            } else {
1058                mTmpValue = null;
1059            }
1060            getValue(id, value, true);
1061        }
1062        try {
1063            return mAssets.openNonAssetFd(
1064                value.assetCookie, value.string.toString());
1065        } catch (Exception e) {
1066            NotFoundException rnf = new NotFoundException(
1067                "File " + value.string.toString()
1068                + " from drawable resource ID #0x"
1069                + Integer.toHexString(id));
1070            rnf.initCause(e);
1071            throw rnf;
1072        } finally {
1073            synchronized (mAccessLock) {
1074                if (mTmpValue == null) {
1075                    mTmpValue = value;
1076                }
1077            }
1078        }
1079    }
1080
1081    /**
1082     * Return the raw data associated with a particular resource ID.
1083     *
1084     * @param id The desired resource identifier, as generated by the aapt
1085     *           tool. This integer encodes the package, type, and resource
1086     *           entry. The value 0 is an invalid identifier.
1087     * @param outValue Object in which to place the resource data.
1088     * @param resolveRefs If true, a resource that is a reference to another
1089     *                    resource will be followed so that you receive the
1090     *                    actual final resource data.  If false, the TypedValue
1091     *                    will be filled in with the reference itself.
1092     *
1093     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1094     *
1095     */
1096    public void getValue(int id, TypedValue outValue, boolean resolveRefs)
1097            throws NotFoundException {
1098        boolean found = mAssets.getResourceValue(id, 0, outValue, resolveRefs);
1099        if (found) {
1100            return;
1101        }
1102        throw new NotFoundException("Resource ID #0x"
1103                                    + Integer.toHexString(id));
1104    }
1105
1106    /**
1107     * Get the raw value associated with a resource with associated density.
1108     *
1109     * @param id resource identifier
1110     * @param density density in DPI
1111     * @param resolveRefs If true, a resource that is a reference to another
1112     *            resource will be followed so that you receive the actual final
1113     *            resource data. If false, the TypedValue will be filled in with
1114     *            the reference itself.
1115     * @throws NotFoundException Throws NotFoundException if the given ID does
1116     *             not exist.
1117     * @see #getValue(String, TypedValue, boolean)
1118     */
1119    public void getValueForDensity(int id, int density, TypedValue outValue, boolean resolveRefs)
1120            throws NotFoundException {
1121        boolean found = mAssets.getResourceValue(id, density, outValue, resolveRefs);
1122        if (found) {
1123            return;
1124        }
1125        throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id));
1126    }
1127
1128    /**
1129     * Return the raw data associated with a particular resource ID.
1130     * See getIdentifier() for information on how names are mapped to resource
1131     * IDs, and getString(int) for information on how string resources are
1132     * retrieved.
1133     *
1134     * <p>Note: use of this function is discouraged.  It is much more
1135     * efficient to retrieve resources by identifier than by name.
1136     *
1137     * @param name The name of the desired resource.  This is passed to
1138     *             getIdentifier() with a default type of "string".
1139     * @param outValue Object in which to place the resource data.
1140     * @param resolveRefs If true, a resource that is a reference to another
1141     *                    resource will be followed so that you receive the
1142     *                    actual final resource data.  If false, the TypedValue
1143     *                    will be filled in with the reference itself.
1144     *
1145     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1146     *
1147     */
1148    public void getValue(String name, TypedValue outValue, boolean resolveRefs)
1149            throws NotFoundException {
1150        int id = getIdentifier(name, "string", null);
1151        if (id != 0) {
1152            getValue(id, outValue, resolveRefs);
1153            return;
1154        }
1155        throw new NotFoundException("String resource name " + name);
1156    }
1157
1158    /**
1159     * This class holds the current attribute values for a particular theme.
1160     * In other words, a Theme is a set of values for resource attributes;
1161     * these are used in conjunction with {@link TypedArray}
1162     * to resolve the final value for an attribute.
1163     *
1164     * <p>The Theme's attributes come into play in two ways: (1) a styled
1165     * attribute can explicit reference a value in the theme through the
1166     * "?themeAttribute" syntax; (2) if no value has been defined for a
1167     * particular styled attribute, as a last resort we will try to find that
1168     * attribute's value in the Theme.
1169     *
1170     * <p>You will normally use the {@link #obtainStyledAttributes} APIs to
1171     * retrieve XML attributes with style and theme information applied.
1172     */
1173    public final class Theme {
1174        /**
1175         * Place new attribute values into the theme.  The style resource
1176         * specified by <var>resid</var> will be retrieved from this Theme's
1177         * resources, its values placed into the Theme object.
1178         *
1179         * <p>The semantics of this function depends on the <var>force</var>
1180         * argument:  If false, only values that are not already defined in
1181         * the theme will be copied from the system resource; otherwise, if
1182         * any of the style's attributes are already defined in the theme, the
1183         * current values in the theme will be overwritten.
1184         *
1185         * @param resid The resource ID of a style resource from which to
1186         *              obtain attribute values.
1187         * @param force If true, values in the style resource will always be
1188         *              used in the theme; otherwise, they will only be used
1189         *              if not already defined in the theme.
1190         */
1191        public void applyStyle(int resid, boolean force) {
1192            AssetManager.applyThemeStyle(mTheme, resid, force);
1193        }
1194
1195        /**
1196         * Set this theme to hold the same contents as the theme
1197         * <var>other</var>.  If both of these themes are from the same
1198         * Resources object, they will be identical after this function
1199         * returns.  If they are from different Resources, only the resources
1200         * they have in common will be set in this theme.
1201         *
1202         * @param other The existing Theme to copy from.
1203         */
1204        public void setTo(Theme other) {
1205            AssetManager.copyTheme(mTheme, other.mTheme);
1206        }
1207
1208        /**
1209         * Return a TypedArray holding the values defined by
1210         * <var>Theme</var> which are listed in <var>attrs</var>.
1211         *
1212         * <p>Be sure to call {@link TypedArray#recycle() TypedArray.recycle()} when you are done
1213         * with the array.
1214         *
1215         * @param attrs The desired attributes.
1216         *
1217         * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1218         *
1219         * @return Returns a TypedArray holding an array of the attribute values.
1220         * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
1221         * when done with it.
1222         *
1223         * @see Resources#obtainAttributes
1224         * @see #obtainStyledAttributes(int, int[])
1225         * @see #obtainStyledAttributes(AttributeSet, int[], int, int)
1226         */
1227        public TypedArray obtainStyledAttributes(int[] attrs) {
1228            int len = attrs.length;
1229            TypedArray array = getCachedStyledAttributes(len);
1230            array.mRsrcs = attrs;
1231            AssetManager.applyStyle(mTheme, 0, 0, 0, attrs,
1232                    array.mData, array.mIndices);
1233            return array;
1234        }
1235
1236        /**
1237         * Return a TypedArray holding the values defined by the style
1238         * resource <var>resid</var> which are listed in <var>attrs</var>.
1239         *
1240         * <p>Be sure to call {@link TypedArray#recycle() TypedArray.recycle()} when you are done
1241         * with the array.
1242         *
1243         * @param resid The desired style resource.
1244         * @param attrs The desired attributes in the style.
1245         *
1246         * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1247         *
1248         * @return Returns a TypedArray holding an array of the attribute values.
1249         * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
1250         * when done with it.
1251         *
1252         * @see Resources#obtainAttributes
1253         * @see #obtainStyledAttributes(int[])
1254         * @see #obtainStyledAttributes(AttributeSet, int[], int, int)
1255         */
1256        public TypedArray obtainStyledAttributes(int resid, int[] attrs)
1257                throws NotFoundException {
1258            int len = attrs.length;
1259            TypedArray array = getCachedStyledAttributes(len);
1260            array.mRsrcs = attrs;
1261
1262            AssetManager.applyStyle(mTheme, 0, resid, 0, attrs,
1263                    array.mData, array.mIndices);
1264            if (false) {
1265                int[] data = array.mData;
1266
1267                System.out.println("**********************************************************");
1268                System.out.println("**********************************************************");
1269                System.out.println("**********************************************************");
1270                System.out.println("Attributes:");
1271                String s = "  Attrs:";
1272                int i;
1273                for (i=0; i<attrs.length; i++) {
1274                    s = s + " 0x" + Integer.toHexString(attrs[i]);
1275                }
1276                System.out.println(s);
1277                s = "  Found:";
1278                TypedValue value = new TypedValue();
1279                for (i=0; i<attrs.length; i++) {
1280                    int d = i*AssetManager.STYLE_NUM_ENTRIES;
1281                    value.type = data[d+AssetManager.STYLE_TYPE];
1282                    value.data = data[d+AssetManager.STYLE_DATA];
1283                    value.assetCookie = data[d+AssetManager.STYLE_ASSET_COOKIE];
1284                    value.resourceId = data[d+AssetManager.STYLE_RESOURCE_ID];
1285                    s = s + " 0x" + Integer.toHexString(attrs[i])
1286                        + "=" + value;
1287                }
1288                System.out.println(s);
1289            }
1290            return array;
1291        }
1292
1293        /**
1294         * Return a TypedArray holding the attribute values in
1295         * <var>set</var>
1296         * that are listed in <var>attrs</var>.  In addition, if the given
1297         * AttributeSet specifies a style class (through the "style" attribute),
1298         * that style will be applied on top of the base attributes it defines.
1299         *
1300         * <p>Be sure to call {@link TypedArray#recycle() TypedArray.recycle()} when you are done
1301         * with the array.
1302         *
1303         * <p>When determining the final value of a particular attribute, there
1304         * are four inputs that come into play:</p>
1305         *
1306         * <ol>
1307         *     <li> Any attribute values in the given AttributeSet.
1308         *     <li> The style resource specified in the AttributeSet (named
1309         *     "style").
1310         *     <li> The default style specified by <var>defStyleAttr</var> and
1311         *     <var>defStyleRes</var>
1312         *     <li> The base values in this theme.
1313         * </ol>
1314         *
1315         * <p>Each of these inputs is considered in-order, with the first listed
1316         * taking precedence over the following ones.  In other words, if in the
1317         * AttributeSet you have supplied <code>&lt;Button
1318         * textColor="#ff000000"&gt;</code>, then the button's text will
1319         * <em>always</em> be black, regardless of what is specified in any of
1320         * the styles.
1321         *
1322         * @param set The base set of attribute values.  May be null.
1323         * @param attrs The desired attributes to be retrieved.
1324         * @param defStyleAttr An attribute in the current theme that contains a
1325         *                     reference to a style resource that supplies
1326         *                     defaults values for the TypedArray.  Can be
1327         *                     0 to not look for defaults.
1328         * @param defStyleRes A resource identifier of a style resource that
1329         *                    supplies default values for the TypedArray,
1330         *                    used only if defStyleAttr is 0 or can not be found
1331         *                    in the theme.  Can be 0 to not look for defaults.
1332         *
1333         * @return Returns a TypedArray holding an array of the attribute values.
1334         * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
1335         * when done with it.
1336         *
1337         * @see Resources#obtainAttributes
1338         * @see #obtainStyledAttributes(int[])
1339         * @see #obtainStyledAttributes(int, int[])
1340         */
1341        public TypedArray obtainStyledAttributes(AttributeSet set,
1342                int[] attrs, int defStyleAttr, int defStyleRes) {
1343            int len = attrs.length;
1344            TypedArray array = getCachedStyledAttributes(len);
1345
1346            // XXX note that for now we only work with compiled XML files.
1347            // To support generic XML files we will need to manually parse
1348            // out the attributes from the XML file (applying type information
1349            // contained in the resources and such).
1350            XmlBlock.Parser parser = (XmlBlock.Parser)set;
1351            AssetManager.applyStyle(
1352                mTheme, defStyleAttr, defStyleRes,
1353                parser != null ? parser.mParseState : 0, attrs,
1354                        array.mData, array.mIndices);
1355
1356            array.mRsrcs = attrs;
1357            array.mXml = parser;
1358
1359            if (false) {
1360                int[] data = array.mData;
1361
1362                System.out.println("Attributes:");
1363                String s = "  Attrs:";
1364                int i;
1365                for (i=0; i<set.getAttributeCount(); i++) {
1366                    s = s + " " + set.getAttributeName(i);
1367                    int id = set.getAttributeNameResource(i);
1368                    if (id != 0) {
1369                        s = s + "(0x" + Integer.toHexString(id) + ")";
1370                    }
1371                    s = s + "=" + set.getAttributeValue(i);
1372                }
1373                System.out.println(s);
1374                s = "  Found:";
1375                TypedValue value = new TypedValue();
1376                for (i=0; i<attrs.length; i++) {
1377                    int d = i*AssetManager.STYLE_NUM_ENTRIES;
1378                    value.type = data[d+AssetManager.STYLE_TYPE];
1379                    value.data = data[d+AssetManager.STYLE_DATA];
1380                    value.assetCookie = data[d+AssetManager.STYLE_ASSET_COOKIE];
1381                    value.resourceId = data[d+AssetManager.STYLE_RESOURCE_ID];
1382                    s = s + " 0x" + Integer.toHexString(attrs[i])
1383                        + "=" + value;
1384                }
1385                System.out.println(s);
1386            }
1387
1388            return array;
1389        }
1390
1391        /**
1392         * Retrieve the value of an attribute in the Theme.  The contents of
1393         * <var>outValue</var> are ultimately filled in by
1394         * {@link Resources#getValue}.
1395         *
1396         * @param resid The resource identifier of the desired theme
1397         *              attribute.
1398         * @param outValue Filled in with the ultimate resource value supplied
1399         *                 by the attribute.
1400         * @param resolveRefs If true, resource references will be walked; if
1401         *                    false, <var>outValue</var> may be a
1402         *                    TYPE_REFERENCE.  In either case, it will never
1403         *                    be a TYPE_ATTRIBUTE.
1404         *
1405         * @return boolean Returns true if the attribute was found and
1406         *         <var>outValue</var> is valid, else false.
1407         */
1408        public boolean resolveAttribute(int resid, TypedValue outValue,
1409                boolean resolveRefs) {
1410            boolean got = mAssets.getThemeValue(mTheme, resid, outValue, resolveRefs);
1411            if (false) {
1412                System.out.println(
1413                    "resolveAttribute #" + Integer.toHexString(resid)
1414                    + " got=" + got + ", type=0x" + Integer.toHexString(outValue.type)
1415                    + ", data=0x" + Integer.toHexString(outValue.data));
1416            }
1417            return got;
1418        }
1419
1420        /**
1421         * Print contents of this theme out to the log.  For debugging only.
1422         *
1423         * @param priority The log priority to use.
1424         * @param tag The log tag to use.
1425         * @param prefix Text to prefix each line printed.
1426         */
1427        public void dump(int priority, String tag, String prefix) {
1428            AssetManager.dumpTheme(mTheme, priority, tag, prefix);
1429        }
1430
1431        protected void finalize() throws Throwable {
1432            super.finalize();
1433            mAssets.releaseTheme(mTheme);
1434        }
1435
1436        /*package*/ Theme() {
1437            mAssets = Resources.this.mAssets;
1438            mTheme = mAssets.createTheme();
1439        }
1440
1441        private final AssetManager mAssets;
1442        private final int mTheme;
1443    }
1444
1445    /**
1446     * Generate a new Theme object for this set of Resources.  It initially
1447     * starts out empty.
1448     *
1449     * @return Theme The newly created Theme container.
1450     */
1451    public final Theme newTheme() {
1452        return new Theme();
1453    }
1454
1455    /**
1456     * Retrieve a set of basic attribute values from an AttributeSet, not
1457     * performing styling of them using a theme and/or style resources.
1458     *
1459     * @param set The current attribute values to retrieve.
1460     * @param attrs The specific attributes to be retrieved.
1461     * @return Returns a TypedArray holding an array of the attribute values.
1462     * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
1463     * when done with it.
1464     *
1465     * @see Theme#obtainStyledAttributes(AttributeSet, int[], int, int)
1466     */
1467    public TypedArray obtainAttributes(AttributeSet set, int[] attrs) {
1468        int len = attrs.length;
1469        TypedArray array = getCachedStyledAttributes(len);
1470
1471        // XXX note that for now we only work with compiled XML files.
1472        // To support generic XML files we will need to manually parse
1473        // out the attributes from the XML file (applying type information
1474        // contained in the resources and such).
1475        XmlBlock.Parser parser = (XmlBlock.Parser)set;
1476        mAssets.retrieveAttributes(parser.mParseState, attrs,
1477                array.mData, array.mIndices);
1478
1479        array.mRsrcs = attrs;
1480        array.mXml = parser;
1481
1482        return array;
1483    }
1484
1485    /**
1486     * Store the newly updated configuration.
1487     */
1488    public void updateConfiguration(Configuration config,
1489            DisplayMetrics metrics) {
1490        updateConfiguration(config, metrics, null);
1491    }
1492
1493    /**
1494     * @hide
1495     */
1496    public void updateConfiguration(Configuration config,
1497            DisplayMetrics metrics, CompatibilityInfo compat) {
1498        synchronized (mAccessLock) {
1499            if (false) {
1500                Slog.i(TAG, "**** Updating config of " + this + ": old config is "
1501                        + mConfiguration + " old compat is " + mCompatibilityInfo);
1502                Slog.i(TAG, "**** Updating config of " + this + ": new config is "
1503                        + config + " new compat is " + compat);
1504            }
1505            if (compat != null) {
1506                mCompatibilityInfo = compat;
1507            }
1508            if (metrics != null) {
1509                mMetrics.setTo(metrics);
1510            }
1511            // NOTE: We should re-arrange this code to create a Display
1512            // with the CompatibilityInfo that is used everywhere we deal
1513            // with the display in relation to this app, rather than
1514            // doing the conversion here.  This impl should be okay because
1515            // we make sure to return a compatible display in the places
1516            // where there are public APIs to retrieve the display...  but
1517            // it would be cleaner and more maintainble to just be
1518            // consistently dealing with a compatible display everywhere in
1519            // the framework.
1520            if (mCompatibilityInfo != null) {
1521                mCompatibilityInfo.applyToDisplayMetrics(mMetrics);
1522            }
1523            int configChanges = 0xfffffff;
1524            if (config != null) {
1525                mTmpConfig.setTo(config);
1526                int density = config.densityDpi;
1527                if (density == Configuration.DENSITY_DPI_UNDEFINED) {
1528                    density = mMetrics.noncompatDensityDpi;
1529                }
1530                if (mCompatibilityInfo != null) {
1531                    mCompatibilityInfo.applyToConfiguration(density, mTmpConfig);
1532                }
1533                if (mTmpConfig.locale == null) {
1534                    mTmpConfig.locale = Locale.getDefault();
1535                    mTmpConfig.setLayoutDirection(mTmpConfig.locale);
1536                }
1537                configChanges = mConfiguration.updateFrom(mTmpConfig);
1538                configChanges = ActivityInfo.activityInfoConfigToNative(configChanges);
1539            }
1540            if (mConfiguration.locale == null) {
1541                mConfiguration.locale = Locale.getDefault();
1542                mConfiguration.setLayoutDirection(mConfiguration.locale);
1543            }
1544            if (mConfiguration.densityDpi != Configuration.DENSITY_DPI_UNDEFINED) {
1545                mMetrics.densityDpi = mConfiguration.densityDpi;
1546                mMetrics.density = mConfiguration.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
1547            }
1548            mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale;
1549
1550            String locale = null;
1551            if (mConfiguration.locale != null) {
1552                locale = mConfiguration.locale.getLanguage();
1553                if (mConfiguration.locale.getCountry() != null) {
1554                    locale += "-" + mConfiguration.locale.getCountry();
1555                }
1556            }
1557            int width, height;
1558            if (mMetrics.widthPixels >= mMetrics.heightPixels) {
1559                width = mMetrics.widthPixels;
1560                height = mMetrics.heightPixels;
1561            } else {
1562                //noinspection SuspiciousNameCombination
1563                width = mMetrics.heightPixels;
1564                //noinspection SuspiciousNameCombination
1565                height = mMetrics.widthPixels;
1566            }
1567            int keyboardHidden = mConfiguration.keyboardHidden;
1568            if (keyboardHidden == Configuration.KEYBOARDHIDDEN_NO
1569                    && mConfiguration.hardKeyboardHidden
1570                            == Configuration.HARDKEYBOARDHIDDEN_YES) {
1571                keyboardHidden = Configuration.KEYBOARDHIDDEN_SOFT;
1572            }
1573            mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
1574                    locale, mConfiguration.orientation,
1575                    mConfiguration.touchscreen,
1576                    mConfiguration.densityDpi, mConfiguration.keyboard,
1577                    keyboardHidden, mConfiguration.navigation, width, height,
1578                    mConfiguration.smallestScreenWidthDp,
1579                    mConfiguration.screenWidthDp, mConfiguration.screenHeightDp,
1580                    mConfiguration.screenLayout, mConfiguration.uiMode,
1581                    Build.VERSION.RESOURCES_SDK_INT);
1582
1583            if (DEBUG_CONFIG) {
1584                Slog.i(TAG, "**** Updating config of " + this + ": final config is " + mConfiguration
1585                        + " final compat is " + mCompatibilityInfo);
1586            }
1587
1588            clearDrawableCacheLocked(mDrawableCache, configChanges);
1589            clearDrawableCacheLocked(mColorDrawableCache, configChanges);
1590
1591            mColorStateListCache.clear();
1592
1593            flushLayoutCache();
1594        }
1595        synchronized (sSync) {
1596            if (mPluralRule != null) {
1597                mPluralRule = NativePluralRules.forLocale(config.locale);
1598            }
1599        }
1600    }
1601
1602    private void clearDrawableCacheLocked(
1603            LongSparseArray<WeakReference<ConstantState>> cache,
1604            int configChanges) {
1605        int N = cache.size();
1606        if (DEBUG_CONFIG) {
1607            Log.d(TAG, "Cleaning up drawables config changes: 0x"
1608                    + Integer.toHexString(configChanges));
1609        }
1610        for (int i=0; i<N; i++) {
1611            WeakReference<Drawable.ConstantState> ref = cache.valueAt(i);
1612            if (ref != null) {
1613                Drawable.ConstantState cs = ref.get();
1614                if (cs != null) {
1615                    if (Configuration.needNewResources(
1616                            configChanges, cs.getChangingConfigurations())) {
1617                        if (DEBUG_CONFIG) {
1618                            Log.d(TAG, "FLUSHING #0x"
1619                                    + Long.toHexString(mDrawableCache.keyAt(i))
1620                                    + " / " + cs + " with changes: 0x"
1621                                    + Integer.toHexString(cs.getChangingConfigurations()));
1622                        }
1623                        cache.setValueAt(i, null);
1624                    } else if (DEBUG_CONFIG) {
1625                        Log.d(TAG, "(Keeping #0x"
1626                                + Long.toHexString(cache.keyAt(i))
1627                                + " / " + cs + " with changes: 0x"
1628                                + Integer.toHexString(cs.getChangingConfigurations())
1629                                + ")");
1630                    }
1631                }
1632            }
1633        }
1634    }
1635
1636    /**
1637     * Update the system resources configuration if they have previously
1638     * been initialized.
1639     *
1640     * @hide
1641     */
1642    public static void updateSystemConfiguration(Configuration config, DisplayMetrics metrics,
1643            CompatibilityInfo compat) {
1644        if (mSystem != null) {
1645            mSystem.updateConfiguration(config, metrics, compat);
1646            //Log.i(TAG, "Updated system resources " + mSystem
1647            //        + ": " + mSystem.getConfiguration());
1648        }
1649    }
1650
1651    /**
1652     * @hide
1653     */
1654    public static void updateSystemConfiguration(Configuration config, DisplayMetrics metrics) {
1655        updateSystemConfiguration(config, metrics, null);
1656    }
1657
1658    /**
1659     * Return the current display metrics that are in effect for this resource
1660     * object.  The returned object should be treated as read-only.
1661     *
1662     * @return The resource's current display metrics.
1663     */
1664    public DisplayMetrics getDisplayMetrics() {
1665        if (DEBUG_CONFIG) Slog.v(TAG, "Returning DisplayMetrics: " + mMetrics.widthPixels
1666                + "x" + mMetrics.heightPixels + " " + mMetrics.density);
1667        return mMetrics;
1668    }
1669
1670    /**
1671     * Return the current configuration that is in effect for this resource
1672     * object.  The returned object should be treated as read-only.
1673     *
1674     * @return The resource's current configuration.
1675     */
1676    public Configuration getConfiguration() {
1677        return mConfiguration;
1678    }
1679
1680    /**
1681     * Return the compatibility mode information for the application.
1682     * The returned object should be treated as read-only.
1683     *
1684     * @return compatibility info.
1685     * @hide
1686     */
1687    public CompatibilityInfo getCompatibilityInfo() {
1688        return mCompatibilityInfo != null ? mCompatibilityInfo
1689                : CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
1690    }
1691
1692    /**
1693     * This is just for testing.
1694     * @hide
1695     */
1696    public void setCompatibilityInfo(CompatibilityInfo ci) {
1697        mCompatibilityInfo = ci;
1698        updateConfiguration(mConfiguration, mMetrics);
1699    }
1700
1701    /**
1702     * Return a resource identifier for the given resource name.  A fully
1703     * qualified resource name is of the form "package:type/entry".  The first
1704     * two components (package and type) are optional if defType and
1705     * defPackage, respectively, are specified here.
1706     *
1707     * <p>Note: use of this function is discouraged.  It is much more
1708     * efficient to retrieve resources by identifier than by name.
1709     *
1710     * @param name The name of the desired resource.
1711     * @param defType Optional default resource type to find, if "type/" is
1712     *                not included in the name.  Can be null to require an
1713     *                explicit type.
1714     * @param defPackage Optional default package to find, if "package:" is
1715     *                   not included in the name.  Can be null to require an
1716     *                   explicit package.
1717     *
1718     * @return int The associated resource identifier.  Returns 0 if no such
1719     *         resource was found.  (0 is not a valid resource ID.)
1720     */
1721    public int getIdentifier(String name, String defType, String defPackage) {
1722        if (name == null) {
1723            throw new NullPointerException("name is null");
1724        }
1725        try {
1726            return Integer.parseInt(name);
1727        } catch (Exception e) {
1728            // Ignore
1729        }
1730        return mAssets.getResourceIdentifier(name, defType, defPackage);
1731    }
1732
1733    /**
1734     * Return the full name for a given resource identifier.  This name is
1735     * a single string of the form "package:type/entry".
1736     *
1737     * @param resid The resource identifier whose name is to be retrieved.
1738     *
1739     * @return A string holding the name of the resource.
1740     *
1741     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1742     *
1743     * @see #getResourcePackageName
1744     * @see #getResourceTypeName
1745     * @see #getResourceEntryName
1746     */
1747    public String getResourceName(int resid) throws NotFoundException {
1748        String str = mAssets.getResourceName(resid);
1749        if (str != null) return str;
1750        throw new NotFoundException("Unable to find resource ID #0x"
1751                + Integer.toHexString(resid));
1752    }
1753
1754    /**
1755     * Return the package name for a given resource identifier.
1756     *
1757     * @param resid The resource identifier whose package name is to be
1758     * retrieved.
1759     *
1760     * @return A string holding the package name of the resource.
1761     *
1762     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1763     *
1764     * @see #getResourceName
1765     */
1766    public String getResourcePackageName(int resid) throws NotFoundException {
1767        String str = mAssets.getResourcePackageName(resid);
1768        if (str != null) return str;
1769        throw new NotFoundException("Unable to find resource ID #0x"
1770                + Integer.toHexString(resid));
1771    }
1772
1773    /**
1774     * Return the type name for a given resource identifier.
1775     *
1776     * @param resid The resource identifier whose type name is to be
1777     * retrieved.
1778     *
1779     * @return A string holding the type name of the resource.
1780     *
1781     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1782     *
1783     * @see #getResourceName
1784     */
1785    public String getResourceTypeName(int resid) throws NotFoundException {
1786        String str = mAssets.getResourceTypeName(resid);
1787        if (str != null) return str;
1788        throw new NotFoundException("Unable to find resource ID #0x"
1789                + Integer.toHexString(resid));
1790    }
1791
1792    /**
1793     * Return the entry name for a given resource identifier.
1794     *
1795     * @param resid The resource identifier whose entry name is to be
1796     * retrieved.
1797     *
1798     * @return A string holding the entry name of the resource.
1799     *
1800     * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1801     *
1802     * @see #getResourceName
1803     */
1804    public String getResourceEntryName(int resid) throws NotFoundException {
1805        String str = mAssets.getResourceEntryName(resid);
1806        if (str != null) return str;
1807        throw new NotFoundException("Unable to find resource ID #0x"
1808                + Integer.toHexString(resid));
1809    }
1810
1811    /**
1812     * Parse a series of {@link android.R.styleable#Extra &lt;extra&gt;} tags from
1813     * an XML file.  You call this when you are at the parent tag of the
1814     * extra tags, and it will return once all of the child tags have been parsed.
1815     * This will call {@link #parseBundleExtra} for each extra tag encountered.
1816     *
1817     * @param parser The parser from which to retrieve the extras.
1818     * @param outBundle A Bundle in which to place all parsed extras.
1819     * @throws XmlPullParserException
1820     * @throws IOException
1821     */
1822    public void parseBundleExtras(XmlResourceParser parser, Bundle outBundle)
1823            throws XmlPullParserException, IOException {
1824        int outerDepth = parser.getDepth();
1825        int type;
1826        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
1827               && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
1828            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
1829                continue;
1830            }
1831
1832            String nodeName = parser.getName();
1833            if (nodeName.equals("extra")) {
1834                parseBundleExtra("extra", parser, outBundle);
1835                XmlUtils.skipCurrentTag(parser);
1836
1837            } else {
1838                XmlUtils.skipCurrentTag(parser);
1839            }
1840        }
1841    }
1842
1843    /**
1844     * Parse a name/value pair out of an XML tag holding that data.  The
1845     * AttributeSet must be holding the data defined by
1846     * {@link android.R.styleable#Extra}.  The following value types are supported:
1847     * <ul>
1848     * <li> {@link TypedValue#TYPE_STRING}:
1849     * {@link Bundle#putCharSequence Bundle.putCharSequence()}
1850     * <li> {@link TypedValue#TYPE_INT_BOOLEAN}:
1851     * {@link Bundle#putCharSequence Bundle.putBoolean()}
1852     * <li> {@link TypedValue#TYPE_FIRST_INT}-{@link TypedValue#TYPE_LAST_INT}:
1853     * {@link Bundle#putCharSequence Bundle.putBoolean()}
1854     * <li> {@link TypedValue#TYPE_FLOAT}:
1855     * {@link Bundle#putCharSequence Bundle.putFloat()}
1856     * </ul>
1857     *
1858     * @param tagName The name of the tag these attributes come from; this is
1859     * only used for reporting error messages.
1860     * @param attrs The attributes from which to retrieve the name/value pair.
1861     * @param outBundle The Bundle in which to place the parsed value.
1862     * @throws XmlPullParserException If the attributes are not valid.
1863     */
1864    public void parseBundleExtra(String tagName, AttributeSet attrs,
1865            Bundle outBundle) throws XmlPullParserException {
1866        TypedArray sa = obtainAttributes(attrs,
1867                com.android.internal.R.styleable.Extra);
1868
1869        String name = sa.getString(
1870                com.android.internal.R.styleable.Extra_name);
1871        if (name == null) {
1872            sa.recycle();
1873            throw new XmlPullParserException("<" + tagName
1874                    + "> requires an android:name attribute at "
1875                    + attrs.getPositionDescription());
1876        }
1877
1878        TypedValue v = sa.peekValue(
1879                com.android.internal.R.styleable.Extra_value);
1880        if (v != null) {
1881            if (v.type == TypedValue.TYPE_STRING) {
1882                CharSequence cs = v.coerceToString();
1883                outBundle.putCharSequence(name, cs);
1884            } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) {
1885                outBundle.putBoolean(name, v.data != 0);
1886            } else if (v.type >= TypedValue.TYPE_FIRST_INT
1887                    && v.type <= TypedValue.TYPE_LAST_INT) {
1888                outBundle.putInt(name, v.data);
1889            } else if (v.type == TypedValue.TYPE_FLOAT) {
1890                outBundle.putFloat(name, v.getFloat());
1891            } else {
1892                sa.recycle();
1893                throw new XmlPullParserException("<" + tagName
1894                        + "> only supports string, integer, float, color, and boolean at "
1895                        + attrs.getPositionDescription());
1896            }
1897        } else {
1898            sa.recycle();
1899            throw new XmlPullParserException("<" + tagName
1900                    + "> requires an android:value or android:resource attribute at "
1901                    + attrs.getPositionDescription());
1902        }
1903
1904        sa.recycle();
1905    }
1906
1907    /**
1908     * Retrieve underlying AssetManager storage for these resources.
1909     */
1910    public final AssetManager getAssets() {
1911        return mAssets;
1912    }
1913
1914    /**
1915     * Call this to remove all cached loaded layout resources from the
1916     * Resources object.  Only intended for use with performance testing
1917     * tools.
1918     */
1919    public final void flushLayoutCache() {
1920        synchronized (mCachedXmlBlockIds) {
1921            // First see if this block is in our cache.
1922            final int num = mCachedXmlBlockIds.length;
1923            for (int i=0; i<num; i++) {
1924                mCachedXmlBlockIds[i] = -0;
1925                XmlBlock oldBlock = mCachedXmlBlocks[i];
1926                if (oldBlock != null) {
1927                    oldBlock.close();
1928                }
1929                mCachedXmlBlocks[i] = null;
1930            }
1931        }
1932    }
1933
1934    /**
1935     * Start preloading of resource data using this Resources object.  Only
1936     * for use by the zygote process for loading common system resources.
1937     * {@hide}
1938     */
1939    public final void startPreloading() {
1940        synchronized (sSync) {
1941            if (sPreloaded) {
1942                throw new IllegalStateException("Resources already preloaded");
1943            }
1944            sPreloaded = true;
1945            mPreloading = true;
1946            sPreloadedDensity = DisplayMetrics.DENSITY_DEVICE;
1947            mConfiguration.densityDpi = sPreloadedDensity;
1948            updateConfiguration(null, null);
1949        }
1950    }
1951
1952    /**
1953     * Called by zygote when it is done preloading resources, to change back
1954     * to normal Resources operation.
1955     */
1956    public final void finishPreloading() {
1957        if (mPreloading) {
1958            mPreloading = false;
1959            flushLayoutCache();
1960        }
1961    }
1962
1963    private boolean verifyPreloadConfig(TypedValue value, String name) {
1964        if ((value.changingConfigurations&~(ActivityInfo.CONFIG_FONT_SCALE
1965                | ActivityInfo.CONFIG_DENSITY)) != 0) {
1966            String resName;
1967            try {
1968                resName = getResourceName(value.resourceId);
1969            } catch (NotFoundException e) {
1970                resName = "?";
1971            }
1972            Log.w(TAG, "Preloaded " + name + " resource #0x"
1973                    + Integer.toHexString(value.resourceId)
1974                    + " (" + resName + ") that varies with configuration!!");
1975            return false;
1976        }
1977        return true;
1978    }
1979
1980    /*package*/ Drawable loadDrawable(TypedValue value, int id)
1981            throws NotFoundException {
1982
1983        if (TRACE_FOR_PRELOAD) {
1984            // Log only framework resources
1985            if ((id >>> 24) == 0x1) {
1986                final String name = getResourceName(id);
1987                if (name != null) android.util.Log.d("PreloadDrawable", name);
1988            }
1989        }
1990
1991        boolean isColorDrawable = false;
1992        if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
1993                value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
1994            isColorDrawable = true;
1995        }
1996        final long key = isColorDrawable ? value.data :
1997                (((long) value.assetCookie) << 32) | value.data;
1998
1999        Drawable dr = getCachedDrawable(isColorDrawable ? mColorDrawableCache : mDrawableCache, key);
2000
2001        if (dr != null) {
2002            return dr;
2003        }
2004
2005        Drawable.ConstantState cs = isColorDrawable
2006                ? sPreloadedColorDrawables.get(key)
2007                : (sPreloadedDensity == mConfiguration.densityDpi
2008                        ? sPreloadedDrawables.get(key) : null);
2009        if (cs != null) {
2010            dr = cs.newDrawable(this);
2011        } else {
2012            if (isColorDrawable) {
2013                dr = new ColorDrawable(value.data);
2014            }
2015
2016            if (dr == null) {
2017                if (value.string == null) {
2018                    throw new NotFoundException(
2019                            "Resource is not a Drawable (color or path): " + value);
2020                }
2021
2022                String file = value.string.toString();
2023
2024                if (TRACE_FOR_MISS_PRELOAD) {
2025                    // Log only framework resources
2026                    if ((id >>> 24) == 0x1) {
2027                        final String name = getResourceName(id);
2028                        if (name != null) android.util.Log.d(TAG, "Loading framework drawable #"
2029                                + Integer.toHexString(id) + ": " + name
2030                                + " at " + file);
2031                    }
2032                }
2033
2034                if (DEBUG_LOAD) Log.v(TAG, "Loading drawable for cookie "
2035                        + value.assetCookie + ": " + file);
2036
2037                if (file.endsWith(".xml")) {
2038                    try {
2039                        XmlResourceParser rp = loadXmlResourceParser(
2040                                file, id, value.assetCookie, "drawable");
2041                        dr = Drawable.createFromXml(this, rp);
2042                        rp.close();
2043                    } catch (Exception e) {
2044                        NotFoundException rnf = new NotFoundException(
2045                            "File " + file + " from drawable resource ID #0x"
2046                            + Integer.toHexString(id));
2047                        rnf.initCause(e);
2048                        throw rnf;
2049                    }
2050
2051                } else {
2052                    try {
2053                        InputStream is = mAssets.openNonAsset(
2054                                value.assetCookie, file, AssetManager.ACCESS_STREAMING);
2055        //                System.out.println("Opened file " + file + ": " + is);
2056                        dr = Drawable.createFromResourceStream(this, value, is,
2057                                file, null);
2058                        is.close();
2059        //                System.out.println("Created stream: " + dr);
2060                    } catch (Exception e) {
2061                        NotFoundException rnf = new NotFoundException(
2062                            "File " + file + " from drawable resource ID #0x"
2063                            + Integer.toHexString(id));
2064                        rnf.initCause(e);
2065                        throw rnf;
2066                    }
2067                }
2068            }
2069        }
2070
2071        if (dr != null) {
2072            dr.setChangingConfigurations(value.changingConfigurations);
2073            cs = dr.getConstantState();
2074            if (cs != null) {
2075                if (mPreloading) {
2076                    if (verifyPreloadConfig(value, "drawable")) {
2077                        if (isColorDrawable) {
2078                            sPreloadedColorDrawables.put(key, cs);
2079                        } else {
2080                            sPreloadedDrawables.put(key, cs);
2081                        }
2082                    }
2083                } else {
2084                    synchronized (mAccessLock) {
2085                        //Log.i(TAG, "Saving cached drawable @ #" +
2086                        //        Integer.toHexString(key.intValue())
2087                        //        + " in " + this + ": " + cs);
2088                        if (isColorDrawable) {
2089                            mColorDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs));
2090                        } else {
2091                            mDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs));
2092                        }
2093                    }
2094                }
2095            }
2096        }
2097
2098        return dr;
2099    }
2100
2101    private Drawable getCachedDrawable(
2102            LongSparseArray<WeakReference<ConstantState>> drawableCache,
2103            long key) {
2104        synchronized (mAccessLock) {
2105            WeakReference<Drawable.ConstantState> wr = drawableCache.get(key);
2106            if (wr != null) {   // we have the key
2107                Drawable.ConstantState entry = wr.get();
2108                if (entry != null) {
2109                    //Log.i(TAG, "Returning cached drawable @ #" +
2110                    //        Integer.toHexString(((Integer)key).intValue())
2111                    //        + " in " + this + ": " + entry);
2112                    return entry.newDrawable(this);
2113                }
2114                else {  // our entry has been purged
2115                    drawableCache.delete(key);
2116                }
2117            }
2118        }
2119        return null;
2120    }
2121
2122    /*package*/ ColorStateList loadColorStateList(TypedValue value, int id)
2123            throws NotFoundException {
2124        if (TRACE_FOR_PRELOAD) {
2125            // Log only framework resources
2126            if ((id >>> 24) == 0x1) {
2127                final String name = getResourceName(id);
2128                if (name != null) android.util.Log.d("PreloadColorStateList", name);
2129            }
2130        }
2131
2132        final long key = (((long) value.assetCookie) << 32) | value.data;
2133
2134        ColorStateList csl;
2135
2136        if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
2137                value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
2138
2139            csl = sPreloadedColorStateLists.get(key);
2140            if (csl != null) {
2141                return csl;
2142            }
2143
2144            csl = ColorStateList.valueOf(value.data);
2145            if (mPreloading) {
2146                if (verifyPreloadConfig(value, "color")) {
2147                    sPreloadedColorStateLists.put(key, csl);
2148                }
2149            }
2150
2151            return csl;
2152        }
2153
2154        csl = getCachedColorStateList(key);
2155        if (csl != null) {
2156            return csl;
2157        }
2158
2159        csl = sPreloadedColorStateLists.get(key);
2160        if (csl != null) {
2161            return csl;
2162        }
2163
2164        if (value.string == null) {
2165            throw new NotFoundException(
2166                    "Resource is not a ColorStateList (color or path): " + value);
2167        }
2168
2169        String file = value.string.toString();
2170
2171        if (file.endsWith(".xml")) {
2172            try {
2173                XmlResourceParser rp = loadXmlResourceParser(
2174                        file, id, value.assetCookie, "colorstatelist");
2175                csl = ColorStateList.createFromXml(this, rp);
2176                rp.close();
2177            } catch (Exception e) {
2178                NotFoundException rnf = new NotFoundException(
2179                    "File " + file + " from color state list resource ID #0x"
2180                    + Integer.toHexString(id));
2181                rnf.initCause(e);
2182                throw rnf;
2183            }
2184        } else {
2185            throw new NotFoundException(
2186                    "File " + file + " from drawable resource ID #0x"
2187                    + Integer.toHexString(id) + ": .xml extension required");
2188        }
2189
2190        if (csl != null) {
2191            if (mPreloading) {
2192                if (verifyPreloadConfig(value, "color")) {
2193                    sPreloadedColorStateLists.put(key, csl);
2194                }
2195            } else {
2196                synchronized (mAccessLock) {
2197                    //Log.i(TAG, "Saving cached color state list @ #" +
2198                    //        Integer.toHexString(key.intValue())
2199                    //        + " in " + this + ": " + csl);
2200                    mColorStateListCache.put(key, new WeakReference<ColorStateList>(csl));
2201                }
2202            }
2203        }
2204
2205        return csl;
2206    }
2207
2208    private ColorStateList getCachedColorStateList(long key) {
2209        synchronized (mAccessLock) {
2210            WeakReference<ColorStateList> wr = mColorStateListCache.get(key);
2211            if (wr != null) {   // we have the key
2212                ColorStateList entry = wr.get();
2213                if (entry != null) {
2214                    //Log.i(TAG, "Returning cached color state list @ #" +
2215                    //        Integer.toHexString(((Integer)key).intValue())
2216                    //        + " in " + this + ": " + entry);
2217                    return entry;
2218                } else {  // our entry has been purged
2219                    mColorStateListCache.delete(key);
2220                }
2221            }
2222        }
2223        return null;
2224    }
2225
2226    /*package*/ XmlResourceParser loadXmlResourceParser(int id, String type)
2227            throws NotFoundException {
2228        synchronized (mAccessLock) {
2229            TypedValue value = mTmpValue;
2230            getValue(id, value, true);
2231            if (value.type == TypedValue.TYPE_STRING) {
2232                return loadXmlResourceParser(value.string.toString(), id,
2233                        value.assetCookie, type);
2234            }
2235            throw new NotFoundException(
2236                    "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
2237                    + Integer.toHexString(value.type) + " is not valid");
2238        }
2239    }
2240
2241    /*package*/ XmlResourceParser loadXmlResourceParser(String file, int id,
2242            int assetCookie, String type) throws NotFoundException {
2243        if (id != 0) {
2244            try {
2245                // These may be compiled...
2246                synchronized (mCachedXmlBlockIds) {
2247                    // First see if this block is in our cache.
2248                    final int num = mCachedXmlBlockIds.length;
2249                    for (int i=0; i<num; i++) {
2250                        if (mCachedXmlBlockIds[i] == id) {
2251                            //System.out.println("**** REUSING XML BLOCK!  id="
2252                            //                   + id + ", index=" + i);
2253                            return mCachedXmlBlocks[i].newParser();
2254                        }
2255                    }
2256
2257                    // Not in the cache, create a new block and put it at
2258                    // the next slot in the cache.
2259                    XmlBlock block = mAssets.openXmlBlockAsset(
2260                            assetCookie, file);
2261                    if (block != null) {
2262                        int pos = mLastCachedXmlBlockIndex+1;
2263                        if (pos >= num) pos = 0;
2264                        mLastCachedXmlBlockIndex = pos;
2265                        XmlBlock oldBlock = mCachedXmlBlocks[pos];
2266                        if (oldBlock != null) {
2267                            oldBlock.close();
2268                        }
2269                        mCachedXmlBlockIds[pos] = id;
2270                        mCachedXmlBlocks[pos] = block;
2271                        //System.out.println("**** CACHING NEW XML BLOCK!  id="
2272                        //                   + id + ", index=" + pos);
2273                        return block.newParser();
2274                    }
2275                }
2276            } catch (Exception e) {
2277                NotFoundException rnf = new NotFoundException(
2278                        "File " + file + " from xml type " + type + " resource ID #0x"
2279                        + Integer.toHexString(id));
2280                rnf.initCause(e);
2281                throw rnf;
2282            }
2283        }
2284
2285        throw new NotFoundException(
2286                "File " + file + " from xml type " + type + " resource ID #0x"
2287                + Integer.toHexString(id));
2288    }
2289
2290    private TypedArray getCachedStyledAttributes(int len) {
2291        synchronized (mAccessLock) {
2292            TypedArray attrs = mCachedStyledAttributes;
2293            if (attrs != null) {
2294                mCachedStyledAttributes = null;
2295                if (DEBUG_ATTRIBUTES_CACHE) {
2296                    mLastRetrievedAttrs = new RuntimeException("here");
2297                    mLastRetrievedAttrs.fillInStackTrace();
2298                }
2299
2300                attrs.mLength = len;
2301                int fullLen = len * AssetManager.STYLE_NUM_ENTRIES;
2302                if (attrs.mData.length >= fullLen) {
2303                    return attrs;
2304                }
2305                attrs.mData = new int[fullLen];
2306                attrs.mIndices = new int[1+len];
2307                return attrs;
2308            }
2309            if (DEBUG_ATTRIBUTES_CACHE) {
2310                RuntimeException here = new RuntimeException("here");
2311                here.fillInStackTrace();
2312                if (mLastRetrievedAttrs != null) {
2313                    Log.i(TAG, "Allocated new TypedArray of " + len + " in " + this, here);
2314                    Log.i(TAG, "Last retrieved attributes here", mLastRetrievedAttrs);
2315                }
2316                mLastRetrievedAttrs = here;
2317            }
2318            return new TypedArray(this,
2319                    new int[len*AssetManager.STYLE_NUM_ENTRIES],
2320                    new int[1+len], len);
2321        }
2322    }
2323
2324    private Resources() {
2325        mAssets = AssetManager.getSystem();
2326        // NOTE: Intentionally leaving this uninitialized (all values set
2327        // to zero), so that anyone who tries to do something that requires
2328        // metrics will get a very wrong value.
2329        mConfiguration.setToDefaults();
2330        mMetrics.setToDefaults();
2331        updateConfiguration(null, null);
2332        mAssets.ensureStringBlocks();
2333        mCompatibilityInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
2334    }
2335}
2336