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