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