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