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