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