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