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