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