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