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