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