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      */
    687     public Drawable getDrawableForDensity(int id, int density) throws NotFoundException {
    688         synchronized (mTmpValue) {
    689             TypedValue value = mTmpValue;
    690             getValueForDensity(id, density, value, true);
    691 
    692             /*
    693              * Pretend the requested density is actually the display density. If
    694              * the drawable returned is not the requested density, then force it
    695              * to be scaled later by dividing its density by the ratio of
    696              * requested density to actual device density. Drawables that have
    697              * undefined density or no density don't need to be handled here.
    698              */
    699             if (value.density > 0 && value.density != TypedValue.DENSITY_NONE) {
    700                 if (value.density == density) {
    701                     value.density = DisplayMetrics.DENSITY_DEVICE;
    702                 } else {
    703                     value.density = (value.density * DisplayMetrics.DENSITY_DEVICE) / density;
    704                 }
    705             }
    706 
    707             return loadDrawable(value, id);
    708         }
    709     }
    710 
    711     /**
    712      * Return a movie object associated with the particular resource ID.
    713      * @param id The desired resource identifier, as generated by the aapt
    714      *           tool. This integer encodes the package, type, and resource
    715      *           entry. The value 0 is an invalid identifier.
    716      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
    717      *
    718      */
    719     public Movie getMovie(int id) throws NotFoundException {
    720         InputStream is = openRawResource(id);
    721         Movie movie = Movie.decodeStream(is);
    722         try {
    723             is.close();
    724         }
    725         catch (java.io.IOException e) {
    726             // don't care, since the return value is valid
    727         }
    728         return movie;
    729     }
    730 
    731     /**
    732      * Return a color integer associated with a particular resource ID.
    733      * If the resource holds a complex
    734      * {@link android.content.res.ColorStateList}, then the default color from
    735      * the set is returned.
    736      *
    737      * @param id The desired resource identifier, as generated by the aapt
    738      *           tool. This integer encodes the package, type, and resource
    739      *           entry. The value 0 is an invalid identifier.
    740      *
    741      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
    742      *
    743      * @return Returns a single color value in the form 0xAARRGGBB.
    744      */
    745     public int getColor(int id) throws NotFoundException {
    746         synchronized (mTmpValue) {
    747             TypedValue value = mTmpValue;
    748             getValue(id, value, true);
    749             if (value.type >= TypedValue.TYPE_FIRST_INT
    750                 && value.type <= TypedValue.TYPE_LAST_INT) {
    751                 return value.data;
    752             } else if (value.type == TypedValue.TYPE_STRING) {
    753                 ColorStateList csl = loadColorStateList(mTmpValue, id);
    754                 return csl.getDefaultColor();
    755             }
    756             throw new NotFoundException(
    757                 "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
    758                 + Integer.toHexString(value.type) + " is not valid");
    759         }
    760     }
    761 
    762     /**
    763      * Return a color state list associated with a particular resource ID.  The
    764      * resource may contain either a single raw color value, or a complex
    765      * {@link android.content.res.ColorStateList} holding multiple possible colors.
    766      *
    767      * @param id The desired resource identifier of a {@link ColorStateList},
    768      *        as generated by the aapt tool. This integer encodes the package, type, and resource
    769      *        entry. The value 0 is an invalid identifier.
    770      *
    771      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
    772      *
    773      * @return Returns a ColorStateList object containing either a single
    774      * solid color or multiple colors that can be selected based on a state.
    775      */
    776     public ColorStateList getColorStateList(int id) throws NotFoundException {
    777         synchronized (mTmpValue) {
    778             TypedValue value = mTmpValue;
    779             getValue(id, value, true);
    780             return loadColorStateList(value, id);
    781         }
    782     }
    783 
    784     /**
    785      * Return a boolean associated with a particular resource ID.  This can be
    786      * used with any integral resource value, and will return true if it is
    787      * non-zero.
    788      *
    789      * @param id The desired resource identifier, as generated by the aapt
    790      *           tool. This integer encodes the package, type, and resource
    791      *           entry. The value 0 is an invalid identifier.
    792      *
    793      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
    794      *
    795      * @return Returns the boolean value contained in the resource.
    796      */
    797     public boolean getBoolean(int id) throws NotFoundException {
    798         synchronized (mTmpValue) {
    799             TypedValue value = mTmpValue;
    800             getValue(id, value, true);
    801             if (value.type >= TypedValue.TYPE_FIRST_INT
    802                 && value.type <= TypedValue.TYPE_LAST_INT) {
    803                 return value.data != 0;
    804             }
    805             throw new NotFoundException(
    806                 "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
    807                 + Integer.toHexString(value.type) + " is not valid");
    808         }
    809     }
    810 
    811     /**
    812      * Return an integer associated with a particular resource ID.
    813      *
    814      * @param id The desired resource identifier, as generated by the aapt
    815      *           tool. This integer encodes the package, type, and resource
    816      *           entry. The value 0 is an invalid identifier.
    817      *
    818      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
    819      *
    820      * @return Returns the integer value contained in the resource.
    821      */
    822     public int getInteger(int id) throws NotFoundException {
    823         synchronized (mTmpValue) {
    824             TypedValue value = mTmpValue;
    825             getValue(id, value, true);
    826             if (value.type >= TypedValue.TYPE_FIRST_INT
    827                 && value.type <= TypedValue.TYPE_LAST_INT) {
    828                 return value.data;
    829             }
    830             throw new NotFoundException(
    831                 "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
    832                 + Integer.toHexString(value.type) + " is not valid");
    833         }
    834     }
    835 
    836     /**
    837      * Return an XmlResourceParser through which you can read a view layout
    838      * description for the given resource ID.  This parser has limited
    839      * functionality -- in particular, you can't change its input, and only
    840      * the high-level events are available.
    841      *
    842      * <p>This function is really a simple wrapper for calling
    843      * {@link #getXml} with a layout resource.
    844      *
    845      * @param id The desired resource identifier, as generated by the aapt
    846      *           tool. This integer encodes the package, type, and resource
    847      *           entry. The value 0 is an invalid identifier.
    848      *
    849      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
    850      *
    851      * @return A new parser object through which you can read
    852      *         the XML data.
    853      *
    854      * @see #getXml
    855      */
    856     public XmlResourceParser getLayout(int id) throws NotFoundException {
    857         return loadXmlResourceParser(id, "layout");
    858     }
    859 
    860     /**
    861      * Return an XmlResourceParser through which you can read an animation
    862      * description for the given resource ID.  This parser has limited
    863      * functionality -- in particular, you can't change its input, and only
    864      * the high-level events are available.
    865      *
    866      * <p>This function is really a simple wrapper for calling
    867      * {@link #getXml} with an animation resource.
    868      *
    869      * @param id The desired resource identifier, as generated by the aapt
    870      *           tool. This integer encodes the package, type, and resource
    871      *           entry. The value 0 is an invalid identifier.
    872      *
    873      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
    874      *
    875      * @return A new parser object through which you can read
    876      *         the XML data.
    877      *
    878      * @see #getXml
    879      */
    880     public XmlResourceParser getAnimation(int id) throws NotFoundException {
    881         return loadXmlResourceParser(id, "anim");
    882     }
    883 
    884     /**
    885      * Return an XmlResourceParser through which you can read a generic XML
    886      * resource for the given resource ID.
    887      *
    888      * <p>The XmlPullParser implementation returned here has some limited
    889      * functionality.  In particular, you can't change its input, and only
    890      * high-level parsing events are available (since the document was
    891      * pre-parsed for you at build time, which involved merging text and
    892      * stripping comments).
    893      *
    894      * @param id The desired resource identifier, as generated by the aapt
    895      *           tool. This integer encodes the package, type, and resource
    896      *           entry. The value 0 is an invalid identifier.
    897      *
    898      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
    899      *
    900      * @return A new parser object through which you can read
    901      *         the XML data.
    902      *
    903      * @see android.util.AttributeSet
    904      */
    905     public XmlResourceParser getXml(int id) throws NotFoundException {
    906         return loadXmlResourceParser(id, "xml");
    907     }
    908 
    909     /**
    910      * Open a data stream for reading a raw resource.  This can only be used
    911      * with resources whose value is the name of an asset files -- that is, it can be
    912      * used to open drawable, sound, and raw resources; it will fail on string
    913      * and color resources.
    914      *
    915      * @param id The resource identifier to open, as generated by the appt
    916      *           tool.
    917      *
    918      * @return InputStream Access to the resource data.
    919      *
    920      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
    921      *
    922      */
    923     public InputStream openRawResource(int id) throws NotFoundException {
    924         synchronized (mTmpValue) {
    925             return openRawResource(id, mTmpValue);
    926         }
    927     }
    928 
    929     /**
    930      * Open a data stream for reading a raw resource.  This can only be used
    931      * with resources whose value is the name of an asset file -- that is, it can be
    932      * used to open drawable, sound, and raw resources; it will fail on string
    933      * and color resources.
    934      *
    935      * @param id The resource identifier to open, as generated by the appt tool.
    936      * @param value The TypedValue object to hold the resource information.
    937      *
    938      * @return InputStream Access to the resource data.
    939      *
    940      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
    941      */
    942     public InputStream openRawResource(int id, TypedValue value) throws NotFoundException {
    943         getValue(id, value, true);
    944 
    945         try {
    946             return mAssets.openNonAsset(value.assetCookie, value.string.toString(),
    947                     AssetManager.ACCESS_STREAMING);
    948         } catch (Exception e) {
    949             NotFoundException rnf = new NotFoundException("File " + value.string.toString() +
    950                     " from drawable resource ID #0x" + Integer.toHexString(id));
    951             rnf.initCause(e);
    952             throw rnf;
    953         }
    954     }
    955 
    956     /**
    957      * Open a file descriptor for reading a raw resource.  This can only be used
    958      * with resources whose value is the name of an asset files -- that is, it can be
    959      * used to open drawable, sound, and raw resources; it will fail on string
    960      * and color resources.
    961      *
    962      * <p>This function only works for resources that are stored in the package
    963      * as uncompressed data, which typically includes things like mp3 files
    964      * and png images.
    965      *
    966      * @param id The resource identifier to open, as generated by the appt
    967      *           tool.
    968      *
    969      * @return AssetFileDescriptor A new file descriptor you can use to read
    970      * the resource.  This includes the file descriptor itself, as well as the
    971      * offset and length of data where the resource appears in the file.  A
    972      * null is returned if the file exists but is compressed.
    973      *
    974      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
    975      *
    976      */
    977     public AssetFileDescriptor openRawResourceFd(int id) throws NotFoundException {
    978         synchronized (mTmpValue) {
    979             TypedValue value = mTmpValue;
    980             getValue(id, value, true);
    981 
    982             try {
    983                 return mAssets.openNonAssetFd(
    984                     value.assetCookie, value.string.toString());
    985             } catch (Exception e) {
    986                 NotFoundException rnf = new NotFoundException(
    987                     "File " + value.string.toString()
    988                     + " from drawable resource ID #0x"
    989                     + Integer.toHexString(id));
    990                 rnf.initCause(e);
    991                 throw rnf;
    992             }
    993 
    994         }
    995     }
    996 
    997     /**
    998      * Return the raw data associated with a particular resource ID.
    999      *
   1000      * @param id The desired resource identifier, as generated by the aapt
   1001      *           tool. This integer encodes the package, type, and resource
   1002      *           entry. The value 0 is an invalid identifier.
   1003      * @param outValue Object in which to place the resource data.
   1004      * @param resolveRefs If true, a resource that is a reference to another
   1005      *                    resource will be followed so that you receive the
   1006      *                    actual final resource data.  If false, the TypedValue
   1007      *                    will be filled in with the reference itself.
   1008      *
   1009      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
   1010      *
   1011      */
   1012     public void getValue(int id, TypedValue outValue, boolean resolveRefs)
   1013             throws NotFoundException {
   1014         boolean found = mAssets.getResourceValue(id, 0, outValue, resolveRefs);
   1015         if (found) {
   1016             return;
   1017         }
   1018         throw new NotFoundException("Resource ID #0x"
   1019                                     + Integer.toHexString(id));
   1020     }
   1021 
   1022     /**
   1023      * Get the raw value associated with a resource with associated density.
   1024      *
   1025      * @param id resource identifier
   1026      * @param density density in DPI
   1027      * @param resolveRefs If true, a resource that is a reference to another
   1028      *            resource will be followed so that you receive the actual final
   1029      *            resource data. If false, the TypedValue will be filled in with
   1030      *            the reference itself.
   1031      * @throws NotFoundException Throws NotFoundException if the given ID does
   1032      *             not exist.
   1033      * @see #getValue(String, TypedValue, boolean)
   1034      */
   1035     public void getValueForDensity(int id, int density, TypedValue outValue, boolean resolveRefs)
   1036             throws NotFoundException {
   1037         boolean found = mAssets.getResourceValue(id, density, outValue, resolveRefs);
   1038         if (found) {
   1039             return;
   1040         }
   1041         throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id));
   1042     }
   1043 
   1044     /**
   1045      * Return the raw data associated with a particular resource ID.
   1046      * See getIdentifier() for information on how names are mapped to resource
   1047      * IDs, and getString(int) for information on how string resources are
   1048      * retrieved.
   1049      *
   1050      * <p>Note: use of this function is discouraged.  It is much more
   1051      * efficient to retrieve resources by identifier than by name.
   1052      *
   1053      * @param name The name of the desired resource.  This is passed to
   1054      *             getIdentifier() with a default type of "string".
   1055      * @param outValue Object in which to place the resource data.
   1056      * @param resolveRefs If true, a resource that is a reference to another
   1057      *                    resource will be followed so that you receive the
   1058      *                    actual final resource data.  If false, the TypedValue
   1059      *                    will be filled in with the reference itself.
   1060      *
   1061      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
   1062      *
   1063      */
   1064     public void getValue(String name, TypedValue outValue, boolean resolveRefs)
   1065             throws NotFoundException {
   1066         int id = getIdentifier(name, "string", null);
   1067         if (id != 0) {
   1068             getValue(id, outValue, resolveRefs);
   1069             return;
   1070         }
   1071         throw new NotFoundException("String resource name " + name);
   1072     }
   1073 
   1074     /**
   1075      * This class holds the current attribute values for a particular theme.
   1076      * In other words, a Theme is a set of values for resource attributes;
   1077      * these are used in conjunction with {@link TypedArray}
   1078      * to resolve the final value for an attribute.
   1079      *
   1080      * <p>The Theme's attributes come into play in two ways: (1) a styled
   1081      * attribute can explicit reference a value in the theme through the
   1082      * "?themeAttribute" syntax; (2) if no value has been defined for a
   1083      * particular styled attribute, as a last resort we will try to find that
   1084      * attribute's value in the Theme.
   1085      *
   1086      * <p>You will normally use the {@link #obtainStyledAttributes} APIs to
   1087      * retrieve XML attributes with style and theme information applied.
   1088      */
   1089     public final class Theme {
   1090         /**
   1091          * Place new attribute values into the theme.  The style resource
   1092          * specified by <var>resid</var> will be retrieved from this Theme's
   1093          * resources, its values placed into the Theme object.
   1094          *
   1095          * <p>The semantics of this function depends on the <var>force</var>
   1096          * argument:  If false, only values that are not already defined in
   1097          * the theme will be copied from the system resource; otherwise, if
   1098          * any of the style's attributes are already defined in the theme, the
   1099          * current values in the theme will be overwritten.
   1100          *
   1101          * @param resid The resource ID of a style resource from which to
   1102          *              obtain attribute values.
   1103          * @param force If true, values in the style resource will always be
   1104          *              used in the theme; otherwise, they will only be used
   1105          *              if not already defined in the theme.
   1106          */
   1107         public void applyStyle(int resid, boolean force) {
   1108             AssetManager.applyThemeStyle(mTheme, resid, force);
   1109         }
   1110 
   1111         /**
   1112          * Set this theme to hold the same contents as the theme
   1113          * <var>other</var>.  If both of these themes are from the same
   1114          * Resources object, they will be identical after this function
   1115          * returns.  If they are from different Resources, only the resources
   1116          * they have in common will be set in this theme.
   1117          *
   1118          * @param other The existing Theme to copy from.
   1119          */
   1120         public void setTo(Theme other) {
   1121             AssetManager.copyTheme(mTheme, other.mTheme);
   1122         }
   1123 
   1124         /**
   1125          * Return a StyledAttributes holding the values defined by
   1126          * <var>Theme</var> which are listed in <var>attrs</var>.
   1127          *
   1128          * <p>Be sure to call StyledAttributes.recycle() when you are done with
   1129          * the array.
   1130          *
   1131          * @param attrs The desired attributes.
   1132          *
   1133          * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
   1134          *
   1135          * @return Returns a TypedArray holding an array of the attribute values.
   1136          * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
   1137          * when done with it.
   1138          *
   1139          * @see Resources#obtainAttributes
   1140          * @see #obtainStyledAttributes(int, int[])
   1141          * @see #obtainStyledAttributes(AttributeSet, int[], int, int)
   1142          */
   1143         public TypedArray obtainStyledAttributes(int[] attrs) {
   1144             int len = attrs.length;
   1145             TypedArray array = getCachedStyledAttributes(len);
   1146             array.mRsrcs = attrs;
   1147             AssetManager.applyStyle(mTheme, 0, 0, 0, attrs,
   1148                     array.mData, array.mIndices);
   1149             return array;
   1150         }
   1151 
   1152         /**
   1153          * Return a StyledAttributes holding the values defined by the style
   1154          * resource <var>resid</var> which are listed in <var>attrs</var>.
   1155          *
   1156          * <p>Be sure to call StyledAttributes.recycle() when you are done with
   1157          * the array.
   1158          *
   1159          * @param resid The desired style resource.
   1160          * @param attrs The desired attributes in the style.
   1161          *
   1162          * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
   1163          *
   1164          * @return Returns a TypedArray holding an array of the attribute values.
   1165          * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
   1166          * when done with it.
   1167          *
   1168          * @see Resources#obtainAttributes
   1169          * @see #obtainStyledAttributes(int[])
   1170          * @see #obtainStyledAttributes(AttributeSet, int[], int, int)
   1171          */
   1172         public TypedArray obtainStyledAttributes(int resid, int[] attrs)
   1173                 throws NotFoundException {
   1174             int len = attrs.length;
   1175             TypedArray array = getCachedStyledAttributes(len);
   1176             array.mRsrcs = attrs;
   1177 
   1178             AssetManager.applyStyle(mTheme, 0, resid, 0, attrs,
   1179                     array.mData, array.mIndices);
   1180             if (false) {
   1181                 int[] data = array.mData;
   1182 
   1183                 System.out.println("**********************************************************");
   1184                 System.out.println("**********************************************************");
   1185                 System.out.println("**********************************************************");
   1186                 System.out.println("Attributes:");
   1187                 String s = "  Attrs:";
   1188                 int i;
   1189                 for (i=0; i<attrs.length; i++) {
   1190                     s = s + " 0x" + Integer.toHexString(attrs[i]);
   1191                 }
   1192                 System.out.println(s);
   1193                 s = "  Found:";
   1194                 TypedValue value = new TypedValue();
   1195                 for (i=0; i<attrs.length; i++) {
   1196                     int d = i*AssetManager.STYLE_NUM_ENTRIES;
   1197                     value.type = data[d+AssetManager.STYLE_TYPE];
   1198                     value.data = data[d+AssetManager.STYLE_DATA];
   1199                     value.assetCookie = data[d+AssetManager.STYLE_ASSET_COOKIE];
   1200                     value.resourceId = data[d+AssetManager.STYLE_RESOURCE_ID];
   1201                     s = s + " 0x" + Integer.toHexString(attrs[i])
   1202                         + "=" + value;
   1203                 }
   1204                 System.out.println(s);
   1205             }
   1206             return array;
   1207         }
   1208 
   1209         /**
   1210          * Return a StyledAttributes holding the attribute values in
   1211          * <var>set</var>
   1212          * that are listed in <var>attrs</var>.  In addition, if the given
   1213          * AttributeSet specifies a style class (through the "style" attribute),
   1214          * that style will be applied on top of the base attributes it defines.
   1215          *
   1216          * <p>Be sure to call StyledAttributes.recycle() when you are done with
   1217          * the array.
   1218          *
   1219          * <p>When determining the final value of a particular attribute, there
   1220          * are four inputs that come into play:</p>
   1221          *
   1222          * <ol>
   1223          *     <li> Any attribute values in the given AttributeSet.
   1224          *     <li> The style resource specified in the AttributeSet (named
   1225          *     "style").
   1226          *     <li> The default style specified by <var>defStyleAttr</var> and
   1227          *     <var>defStyleRes</var>
   1228          *     <li> The base values in this theme.
   1229          * </ol>
   1230          *
   1231          * <p>Each of these inputs is considered in-order, with the first listed
   1232          * taking precedence over the following ones.  In other words, if in the
   1233          * AttributeSet you have supplied <code>&lt;Button
   1234          * textColor="#ff000000"&gt;</code>, then the button's text will
   1235          * <em>always</em> be black, regardless of what is specified in any of
   1236          * the styles.
   1237          *
   1238          * @param set The base set of attribute values.  May be null.
   1239          * @param attrs The desired attributes to be retrieved.
   1240          * @param defStyleAttr An attribute in the current theme that contains a
   1241          *                     reference to a style resource that supplies
   1242          *                     defaults values for the StyledAttributes.  Can be
   1243          *                     0 to not look for defaults.
   1244          * @param defStyleRes A resource identifier of a style resource that
   1245          *                    supplies default values for the StyledAttributes,
   1246          *                    used only if defStyleAttr is 0 or can not be found
   1247          *                    in the theme.  Can be 0 to not look for defaults.
   1248          *
   1249          * @return Returns a TypedArray holding an array of the attribute values.
   1250          * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
   1251          * when done with it.
   1252          *
   1253          * @see Resources#obtainAttributes
   1254          * @see #obtainStyledAttributes(int[])
   1255          * @see #obtainStyledAttributes(int, int[])
   1256          */
   1257         public TypedArray obtainStyledAttributes(AttributeSet set,
   1258                 int[] attrs, int defStyleAttr, int defStyleRes) {
   1259             int len = attrs.length;
   1260             TypedArray array = getCachedStyledAttributes(len);
   1261 
   1262             // XXX note that for now we only work with compiled XML files.
   1263             // To support generic XML files we will need to manually parse
   1264             // out the attributes from the XML file (applying type information
   1265             // contained in the resources and such).
   1266             XmlBlock.Parser parser = (XmlBlock.Parser)set;
   1267             AssetManager.applyStyle(
   1268                 mTheme, defStyleAttr, defStyleRes,
   1269                 parser != null ? parser.mParseState : 0, attrs,
   1270                         array.mData, array.mIndices);
   1271 
   1272             array.mRsrcs = attrs;
   1273             array.mXml = parser;
   1274 
   1275             if (false) {
   1276                 int[] data = array.mData;
   1277 
   1278                 System.out.println("Attributes:");
   1279                 String s = "  Attrs:";
   1280                 int i;
   1281                 for (i=0; i<set.getAttributeCount(); i++) {
   1282                     s = s + " " + set.getAttributeName(i);
   1283                     int id = set.getAttributeNameResource(i);
   1284                     if (id != 0) {
   1285                         s = s + "(0x" + Integer.toHexString(id) + ")";
   1286                     }
   1287                     s = s + "=" + set.getAttributeValue(i);
   1288                 }
   1289                 System.out.println(s);
   1290                 s = "  Found:";
   1291                 TypedValue value = new TypedValue();
   1292                 for (i=0; i<attrs.length; i++) {
   1293                     int d = i*AssetManager.STYLE_NUM_ENTRIES;
   1294                     value.type = data[d+AssetManager.STYLE_TYPE];
   1295                     value.data = data[d+AssetManager.STYLE_DATA];
   1296                     value.assetCookie = data[d+AssetManager.STYLE_ASSET_COOKIE];
   1297                     value.resourceId = data[d+AssetManager.STYLE_RESOURCE_ID];
   1298                     s = s + " 0x" + Integer.toHexString(attrs[i])
   1299                         + "=" + value;
   1300                 }
   1301                 System.out.println(s);
   1302             }
   1303 
   1304             return array;
   1305         }
   1306 
   1307         /**
   1308          * Retrieve the value of an attribute in the Theme.  The contents of
   1309          * <var>outValue</var> are ultimately filled in by
   1310          * {@link Resources#getValue}.
   1311          *
   1312          * @param resid The resource identifier of the desired theme
   1313          *              attribute.
   1314          * @param outValue Filled in with the ultimate resource value supplied
   1315          *                 by the attribute.
   1316          * @param resolveRefs If true, resource references will be walked; if
   1317          *                    false, <var>outValue</var> may be a
   1318          *                    TYPE_REFERENCE.  In either case, it will never
   1319          *                    be a TYPE_ATTRIBUTE.
   1320          *
   1321          * @return boolean Returns true if the attribute was found and
   1322          *         <var>outValue</var> is valid, else false.
   1323          */
   1324         public boolean resolveAttribute(int resid, TypedValue outValue,
   1325                 boolean resolveRefs) {
   1326             boolean got = mAssets.getThemeValue(mTheme, resid, outValue, resolveRefs);
   1327             if (false) {
   1328                 System.out.println(
   1329                     "resolveAttribute #" + Integer.toHexString(resid)
   1330                     + " got=" + got + ", type=0x" + Integer.toHexString(outValue.type)
   1331                     + ", data=0x" + Integer.toHexString(outValue.data));
   1332             }
   1333             return got;
   1334         }
   1335 
   1336         /**
   1337          * Print contents of this theme out to the log.  For debugging only.
   1338          *
   1339          * @param priority The log priority to use.
   1340          * @param tag The log tag to use.
   1341          * @param prefix Text to prefix each line printed.
   1342          */
   1343         public void dump(int priority, String tag, String prefix) {
   1344             AssetManager.dumpTheme(mTheme, priority, tag, prefix);
   1345         }
   1346 
   1347         protected void finalize() throws Throwable {
   1348             super.finalize();
   1349             mAssets.releaseTheme(mTheme);
   1350         }
   1351 
   1352         /*package*/ Theme() {
   1353             mAssets = Resources.this.mAssets;
   1354             mTheme = mAssets.createTheme();
   1355         }
   1356 
   1357         private final AssetManager mAssets;
   1358         private final int mTheme;
   1359     }
   1360 
   1361     /**
   1362      * Generate a new Theme object for this set of Resources.  It initially
   1363      * starts out empty.
   1364      *
   1365      * @return Theme The newly created Theme container.
   1366      */
   1367     public final Theme newTheme() {
   1368         return new Theme();
   1369     }
   1370 
   1371     /**
   1372      * Retrieve a set of basic attribute values from an AttributeSet, not
   1373      * performing styling of them using a theme and/or style resources.
   1374      *
   1375      * @param set The current attribute values to retrieve.
   1376      * @param attrs The specific attributes to be retrieved.
   1377      * @return Returns a TypedArray holding an array of the attribute values.
   1378      * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
   1379      * when done with it.
   1380      *
   1381      * @see Theme#obtainStyledAttributes(AttributeSet, int[], int, int)
   1382      */
   1383     public TypedArray obtainAttributes(AttributeSet set, int[] attrs) {
   1384         int len = attrs.length;
   1385         TypedArray array = getCachedStyledAttributes(len);
   1386 
   1387         // XXX note that for now we only work with compiled XML files.
   1388         // To support generic XML files we will need to manually parse
   1389         // out the attributes from the XML file (applying type information
   1390         // contained in the resources and such).
   1391         XmlBlock.Parser parser = (XmlBlock.Parser)set;
   1392         mAssets.retrieveAttributes(parser.mParseState, attrs,
   1393                 array.mData, array.mIndices);
   1394 
   1395         array.mRsrcs = attrs;
   1396         array.mXml = parser;
   1397 
   1398         return array;
   1399     }
   1400 
   1401     /**
   1402      * Store the newly updated configuration.
   1403      */
   1404     public void updateConfiguration(Configuration config,
   1405             DisplayMetrics metrics) {
   1406         updateConfiguration(config, metrics, null);
   1407     }
   1408 
   1409     /**
   1410      * @hide
   1411      */
   1412     public void updateConfiguration(Configuration config,
   1413             DisplayMetrics metrics, CompatibilityInfo compat) {
   1414         synchronized (mTmpValue) {
   1415             if (false) {
   1416                 Slog.i(TAG, "**** Updating config of " + this + ": old config is "
   1417                         + mConfiguration + " old compat is " + mCompatibilityInfo);
   1418                 Slog.i(TAG, "**** Updating config of " + this + ": new config is "
   1419                         + config + " new compat is " + compat);
   1420             }
   1421             if (compat != null) {
   1422                 mCompatibilityInfo = compat;
   1423             }
   1424             if (metrics != null) {
   1425                 mMetrics.setTo(metrics);
   1426             }
   1427             // NOTE: We should re-arrange this code to create a Display
   1428             // with the CompatibilityInfo that is used everywhere we deal
   1429             // with the display in relation to this app, rather than
   1430             // doing the conversion here.  This impl should be okay because
   1431             // we make sure to return a compatible display in the places
   1432             // where there are public APIs to retrieve the display...  but
   1433             // it would be cleaner and more maintainble to just be
   1434             // consistently dealing with a compatible display everywhere in
   1435             // the framework.
   1436             if (mCompatibilityInfo != null) {
   1437                 mCompatibilityInfo.applyToDisplayMetrics(mMetrics);
   1438             }
   1439             int configChanges = 0xfffffff;
   1440             if (config != null) {
   1441                 mTmpConfig.setTo(config);
   1442                 if (mCompatibilityInfo != null) {
   1443                     mCompatibilityInfo.applyToConfiguration(mTmpConfig);
   1444                 }
   1445                 if (mTmpConfig.locale == null) {
   1446                     mTmpConfig.locale = Locale.getDefault();
   1447                 }
   1448                 configChanges = mConfiguration.updateFrom(mTmpConfig);
   1449                 configChanges = ActivityInfo.activityInfoConfigToNative(configChanges);
   1450             }
   1451             if (mConfiguration.locale == null) {
   1452                 mConfiguration.locale = Locale.getDefault();
   1453             }
   1454             mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale;
   1455 
   1456             String locale = null;
   1457             if (mConfiguration.locale != null) {
   1458                 locale = mConfiguration.locale.getLanguage();
   1459                 if (mConfiguration.locale.getCountry() != null) {
   1460                     locale += "-" + mConfiguration.locale.getCountry();
   1461                 }
   1462             }
   1463             int width, height;
   1464             if (mMetrics.widthPixels >= mMetrics.heightPixels) {
   1465                 width = mMetrics.widthPixels;
   1466                 height = mMetrics.heightPixels;
   1467             } else {
   1468                 //noinspection SuspiciousNameCombination
   1469                 width = mMetrics.heightPixels;
   1470                 //noinspection SuspiciousNameCombination
   1471                 height = mMetrics.widthPixels;
   1472             }
   1473             int keyboardHidden = mConfiguration.keyboardHidden;
   1474             if (keyboardHidden == Configuration.KEYBOARDHIDDEN_NO
   1475                     && mConfiguration.hardKeyboardHidden
   1476                             == Configuration.HARDKEYBOARDHIDDEN_YES) {
   1477                 keyboardHidden = Configuration.KEYBOARDHIDDEN_SOFT;
   1478             }
   1479             mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
   1480                     locale, mConfiguration.orientation,
   1481                     mConfiguration.touchscreen,
   1482                     (int)(mMetrics.density*160), mConfiguration.keyboard,
   1483                     keyboardHidden, mConfiguration.navigation, width, height,
   1484                     mConfiguration.smallestScreenWidthDp,
   1485                     mConfiguration.screenWidthDp, mConfiguration.screenHeightDp,
   1486                     mConfiguration.screenLayout, mConfiguration.uiMode,
   1487                     Build.VERSION.RESOURCES_SDK_INT);
   1488 
   1489             if (DEBUG_CONFIG) {
   1490                 Slog.i(TAG, "**** Updating config of " + this + ": final config is " + mConfiguration
   1491                         + " final compat is " + mCompatibilityInfo);
   1492             }
   1493 
   1494             clearDrawableCache(mDrawableCache, configChanges);
   1495             clearDrawableCache(mColorDrawableCache, configChanges);
   1496 
   1497             mColorStateListCache.clear();
   1498 
   1499             flushLayoutCache();
   1500         }
   1501         synchronized (mSync) {
   1502             if (mPluralRule != null) {
   1503                 mPluralRule = NativePluralRules.forLocale(config.locale);
   1504             }
   1505         }
   1506     }
   1507 
   1508     private void clearDrawableCache(
   1509             LongSparseArray<WeakReference<ConstantState>> cache,
   1510             int configChanges) {
   1511         int N = cache.size();
   1512         if (DEBUG_CONFIG) {
   1513             Log.d(TAG, "Cleaning up drawables config changes: 0x"
   1514                     + Integer.toHexString(configChanges));
   1515         }
   1516         for (int i=0; i<N; i++) {
   1517             WeakReference<Drawable.ConstantState> ref = cache.valueAt(i);
   1518             if (ref != null) {
   1519                 Drawable.ConstantState cs = ref.get();
   1520                 if (cs != null) {
   1521                     if (Configuration.needNewResources(
   1522                             configChanges, cs.getChangingConfigurations())) {
   1523                         if (DEBUG_CONFIG) {
   1524                             Log.d(TAG, "FLUSHING #0x"
   1525                                     + Long.toHexString(mDrawableCache.keyAt(i))
   1526                                     + " / " + cs + " with changes: 0x"
   1527                                     + Integer.toHexString(cs.getChangingConfigurations()));
   1528                         }
   1529                         cache.setValueAt(i, null);
   1530                     } else if (DEBUG_CONFIG) {
   1531                         Log.d(TAG, "(Keeping #0x"
   1532                                 + Long.toHexString(cache.keyAt(i))
   1533                                 + " / " + cs + " with changes: 0x"
   1534                                 + Integer.toHexString(cs.getChangingConfigurations())
   1535                                 + ")");
   1536                     }
   1537                 }
   1538             }
   1539         }
   1540     }
   1541 
   1542     /**
   1543      * Update the system resources configuration if they have previously
   1544      * been initialized.
   1545      *
   1546      * @hide
   1547      */
   1548     public static void updateSystemConfiguration(Configuration config, DisplayMetrics metrics,
   1549             CompatibilityInfo compat) {
   1550         if (mSystem != null) {
   1551             mSystem.updateConfiguration(config, metrics, compat);
   1552             //Log.i(TAG, "Updated system resources " + mSystem
   1553             //        + ": " + mSystem.getConfiguration());
   1554         }
   1555     }
   1556 
   1557     /**
   1558      * @hide
   1559      */
   1560     public static void updateSystemConfiguration(Configuration config, DisplayMetrics metrics) {
   1561         updateSystemConfiguration(config, metrics, null);
   1562     }
   1563 
   1564     /**
   1565      * Return the current display metrics that are in effect for this resource
   1566      * object.  The returned object should be treated as read-only.
   1567      *
   1568      * @return The resource's current display metrics.
   1569      */
   1570     public DisplayMetrics getDisplayMetrics() {
   1571         if (DEBUG_CONFIG) Slog.v(TAG, "Returning DisplayMetrics: " + mMetrics.widthPixels
   1572                 + "x" + mMetrics.heightPixels + " " + mMetrics.density);
   1573         return mMetrics;
   1574     }
   1575 
   1576     /**
   1577      * Return the current configuration that is in effect for this resource
   1578      * object.  The returned object should be treated as read-only.
   1579      *
   1580      * @return The resource's current configuration.
   1581      */
   1582     public Configuration getConfiguration() {
   1583         return mConfiguration;
   1584     }
   1585 
   1586     /**
   1587      * Return the compatibility mode information for the application.
   1588      * The returned object should be treated as read-only.
   1589      *
   1590      * @return compatibility info.
   1591      * @hide
   1592      */
   1593     public CompatibilityInfo getCompatibilityInfo() {
   1594         return mCompatibilityInfo != null ? mCompatibilityInfo
   1595                 : CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
   1596     }
   1597 
   1598     /**
   1599      * This is just for testing.
   1600      * @hide
   1601      */
   1602     public void setCompatibilityInfo(CompatibilityInfo ci) {
   1603         mCompatibilityInfo = ci;
   1604         updateConfiguration(mConfiguration, mMetrics);
   1605     }
   1606 
   1607     /**
   1608      * Return a resource identifier for the given resource name.  A fully
   1609      * qualified resource name is of the form "package:type/entry".  The first
   1610      * two components (package and type) are optional if defType and
   1611      * defPackage, respectively, are specified here.
   1612      *
   1613      * <p>Note: use of this function is discouraged.  It is much more
   1614      * efficient to retrieve resources by identifier than by name.
   1615      *
   1616      * @param name The name of the desired resource.
   1617      * @param defType Optional default resource type to find, if "type/" is
   1618      *                not included in the name.  Can be null to require an
   1619      *                explicit type.
   1620      * @param defPackage Optional default package to find, if "package:" is
   1621      *                   not included in the name.  Can be null to require an
   1622      *                   explicit package.
   1623      *
   1624      * @return int The associated resource identifier.  Returns 0 if no such
   1625      *         resource was found.  (0 is not a valid resource ID.)
   1626      */
   1627     public int getIdentifier(String name, String defType, String defPackage) {
   1628         try {
   1629             return Integer.parseInt(name);
   1630         } catch (Exception e) {
   1631             // Ignore
   1632         }
   1633         return mAssets.getResourceIdentifier(name, defType, defPackage);
   1634     }
   1635 
   1636     /**
   1637      * Return the full name for a given resource identifier.  This name is
   1638      * a single string of the form "package:type/entry".
   1639      *
   1640      * @param resid The resource identifier whose name is to be retrieved.
   1641      *
   1642      * @return A string holding the name of the resource.
   1643      *
   1644      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
   1645      *
   1646      * @see #getResourcePackageName
   1647      * @see #getResourceTypeName
   1648      * @see #getResourceEntryName
   1649      */
   1650     public String getResourceName(int resid) throws NotFoundException {
   1651         String str = mAssets.getResourceName(resid);
   1652         if (str != null) return str;
   1653         throw new NotFoundException("Unable to find resource ID #0x"
   1654                 + Integer.toHexString(resid));
   1655     }
   1656 
   1657     /**
   1658      * Return the package name for a given resource identifier.
   1659      *
   1660      * @param resid The resource identifier whose package name is to be
   1661      * retrieved.
   1662      *
   1663      * @return A string holding the package name of the resource.
   1664      *
   1665      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
   1666      *
   1667      * @see #getResourceName
   1668      */
   1669     public String getResourcePackageName(int resid) throws NotFoundException {
   1670         String str = mAssets.getResourcePackageName(resid);
   1671         if (str != null) return str;
   1672         throw new NotFoundException("Unable to find resource ID #0x"
   1673                 + Integer.toHexString(resid));
   1674     }
   1675 
   1676     /**
   1677      * Return the type name for a given resource identifier.
   1678      *
   1679      * @param resid The resource identifier whose type name is to be
   1680      * retrieved.
   1681      *
   1682      * @return A string holding the type name of the resource.
   1683      *
   1684      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
   1685      *
   1686      * @see #getResourceName
   1687      */
   1688     public String getResourceTypeName(int resid) throws NotFoundException {
   1689         String str = mAssets.getResourceTypeName(resid);
   1690         if (str != null) return str;
   1691         throw new NotFoundException("Unable to find resource ID #0x"
   1692                 + Integer.toHexString(resid));
   1693     }
   1694 
   1695     /**
   1696      * Return the entry name for a given resource identifier.
   1697      *
   1698      * @param resid The resource identifier whose entry name is to be
   1699      * retrieved.
   1700      *
   1701      * @return A string holding the entry name of the resource.
   1702      *
   1703      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
   1704      *
   1705      * @see #getResourceName
   1706      */
   1707     public String getResourceEntryName(int resid) throws NotFoundException {
   1708         String str = mAssets.getResourceEntryName(resid);
   1709         if (str != null) return str;
   1710         throw new NotFoundException("Unable to find resource ID #0x"
   1711                 + Integer.toHexString(resid));
   1712     }
   1713 
   1714     /**
   1715      * Parse a series of {@link android.R.styleable#Extra &lt;extra&gt;} tags from
   1716      * an XML file.  You call this when you are at the parent tag of the
   1717      * extra tags, and it will return once all of the child tags have been parsed.
   1718      * This will call {@link #parseBundleExtra} for each extra tag encountered.
   1719      *
   1720      * @param parser The parser from which to retrieve the extras.
   1721      * @param outBundle A Bundle in which to place all parsed extras.
   1722      * @throws XmlPullParserException
   1723      * @throws IOException
   1724      */
   1725     public void parseBundleExtras(XmlResourceParser parser, Bundle outBundle)
   1726             throws XmlPullParserException, IOException {
   1727         int outerDepth = parser.getDepth();
   1728         int type;
   1729         while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
   1730                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
   1731             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
   1732                 continue;
   1733             }
   1734 
   1735             String nodeName = parser.getName();
   1736             if (nodeName.equals("extra")) {
   1737                 parseBundleExtra("extra", parser, outBundle);
   1738                 XmlUtils.skipCurrentTag(parser);
   1739 
   1740             } else {
   1741                 XmlUtils.skipCurrentTag(parser);
   1742             }
   1743         }
   1744     }
   1745 
   1746     /**
   1747      * Parse a name/value pair out of an XML tag holding that data.  The
   1748      * AttributeSet must be holding the data defined by
   1749      * {@link android.R.styleable#Extra}.  The following value types are supported:
   1750      * <ul>
   1751      * <li> {@link TypedValue#TYPE_STRING}:
   1752      * {@link Bundle#putCharSequence Bundle.putCharSequence()}
   1753      * <li> {@link TypedValue#TYPE_INT_BOOLEAN}:
   1754      * {@link Bundle#putCharSequence Bundle.putBoolean()}
   1755      * <li> {@link TypedValue#TYPE_FIRST_INT}-{@link TypedValue#TYPE_LAST_INT}:
   1756      * {@link Bundle#putCharSequence Bundle.putBoolean()}
   1757      * <li> {@link TypedValue#TYPE_FLOAT}:
   1758      * {@link Bundle#putCharSequence Bundle.putFloat()}
   1759      * </ul>
   1760      *
   1761      * @param tagName The name of the tag these attributes come from; this is
   1762      * only used for reporting error messages.
   1763      * @param attrs The attributes from which to retrieve the name/value pair.
   1764      * @param outBundle The Bundle in which to place the parsed value.
   1765      * @throws XmlPullParserException If the attributes are not valid.
   1766      */
   1767     public void parseBundleExtra(String tagName, AttributeSet attrs,
   1768             Bundle outBundle) throws XmlPullParserException {
   1769         TypedArray sa = obtainAttributes(attrs,
   1770                 com.android.internal.R.styleable.Extra);
   1771 
   1772         String name = sa.getString(
   1773                 com.android.internal.R.styleable.Extra_name);
   1774         if (name == null) {
   1775             sa.recycle();
   1776             throw new XmlPullParserException("<" + tagName
   1777                     + "> requires an android:name attribute at "
   1778                     + attrs.getPositionDescription());
   1779         }
   1780 
   1781         TypedValue v = sa.peekValue(
   1782                 com.android.internal.R.styleable.Extra_value);
   1783         if (v != null) {
   1784             if (v.type == TypedValue.TYPE_STRING) {
   1785                 CharSequence cs = v.coerceToString();
   1786                 outBundle.putCharSequence(name, cs);
   1787             } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) {
   1788                 outBundle.putBoolean(name, v.data != 0);
   1789             } else if (v.type >= TypedValue.TYPE_FIRST_INT
   1790                     && v.type <= TypedValue.TYPE_LAST_INT) {
   1791                 outBundle.putInt(name, v.data);
   1792             } else if (v.type == TypedValue.TYPE_FLOAT) {
   1793                 outBundle.putFloat(name, v.getFloat());
   1794             } else {
   1795                 sa.recycle();
   1796                 throw new XmlPullParserException("<" + tagName
   1797                         + "> only supports string, integer, float, color, and boolean at "
   1798                         + attrs.getPositionDescription());
   1799             }
   1800         } else {
   1801             sa.recycle();
   1802             throw new XmlPullParserException("<" + tagName
   1803                     + "> requires an android:value or android:resource attribute at "
   1804                     + attrs.getPositionDescription());
   1805         }
   1806 
   1807         sa.recycle();
   1808     }
   1809 
   1810     /**
   1811      * Retrieve underlying AssetManager storage for these resources.
   1812      */
   1813     public final AssetManager getAssets() {
   1814         return mAssets;
   1815     }
   1816 
   1817     /**
   1818      * Call this to remove all cached loaded layout resources from the
   1819      * Resources object.  Only intended for use with performance testing
   1820      * tools.
   1821      */
   1822     public final void flushLayoutCache() {
   1823         synchronized (mCachedXmlBlockIds) {
   1824             // First see if this block is in our cache.
   1825             final int num = mCachedXmlBlockIds.length;
   1826             for (int i=0; i<num; i++) {
   1827                 mCachedXmlBlockIds[i] = -0;
   1828                 XmlBlock oldBlock = mCachedXmlBlocks[i];
   1829                 if (oldBlock != null) {
   1830                     oldBlock.close();
   1831                 }
   1832                 mCachedXmlBlocks[i] = null;
   1833             }
   1834         }
   1835     }
   1836 
   1837     /**
   1838      * Start preloading of resource data using this Resources object.  Only
   1839      * for use by the zygote process for loading common system resources.
   1840      * {@hide}
   1841      */
   1842     public final void startPreloading() {
   1843         synchronized (mSync) {
   1844             if (mPreloaded) {
   1845                 throw new IllegalStateException("Resources already preloaded");
   1846             }
   1847             mPreloaded = true;
   1848             mPreloading = true;
   1849         }
   1850     }
   1851 
   1852     /**
   1853      * Called by zygote when it is done preloading resources, to change back
   1854      * to normal Resources operation.
   1855      */
   1856     public final void finishPreloading() {
   1857         if (mPreloading) {
   1858             mPreloading = false;
   1859             flushLayoutCache();
   1860         }
   1861     }
   1862 
   1863     /*package*/ Drawable loadDrawable(TypedValue value, int id)
   1864             throws NotFoundException {
   1865 
   1866         if (TRACE_FOR_PRELOAD) {
   1867             // Log only framework resources
   1868             if ((id >>> 24) == 0x1) {
   1869                 final String name = getResourceName(id);
   1870                 if (name != null) android.util.Log.d("PreloadDrawable", name);
   1871             }
   1872         }
   1873 
   1874         final long key = (((long) value.assetCookie) << 32) | value.data;
   1875         boolean isColorDrawable = false;
   1876         if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
   1877                 value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
   1878             isColorDrawable = true;
   1879         }
   1880         Drawable dr = getCachedDrawable(isColorDrawable ? mColorDrawableCache : mDrawableCache, key);
   1881 
   1882         if (dr != null) {
   1883             return dr;
   1884         }
   1885 
   1886         Drawable.ConstantState cs = isColorDrawable ? sPreloadedColorDrawables.get(key) : sPreloadedDrawables.get(key);
   1887         if (cs != null) {
   1888             dr = cs.newDrawable(this);
   1889         } else {
   1890             if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
   1891                     value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
   1892                 dr = new ColorDrawable(value.data);
   1893             }
   1894 
   1895             if (dr == null) {
   1896                 if (value.string == null) {
   1897                     throw new NotFoundException(
   1898                             "Resource is not a Drawable (color or path): " + value);
   1899                 }
   1900 
   1901                 String file = value.string.toString();
   1902 
   1903                 if (TRACE_FOR_MISS_PRELOAD) {
   1904                     // Log only framework resources
   1905                     if ((id >>> 24) == 0x1) {
   1906                         final String name = getResourceName(id);
   1907                         if (name != null) android.util.Log.d(TAG, "Loading framework drawable #"
   1908                                 + Integer.toHexString(id) + ": " + name
   1909                                 + " at " + file);
   1910                     }
   1911                 }
   1912 
   1913                 if (DEBUG_LOAD) Log.v(TAG, "Loading drawable for cookie "
   1914                         + value.assetCookie + ": " + file);
   1915 
   1916                 if (file.endsWith(".xml")) {
   1917                     try {
   1918                         XmlResourceParser rp = loadXmlResourceParser(
   1919                                 file, id, value.assetCookie, "drawable");
   1920                         dr = Drawable.createFromXml(this, rp);
   1921                         rp.close();
   1922                     } catch (Exception e) {
   1923                         NotFoundException rnf = new NotFoundException(
   1924                             "File " + file + " from drawable resource ID #0x"
   1925                             + Integer.toHexString(id));
   1926                         rnf.initCause(e);
   1927                         throw rnf;
   1928                     }
   1929 
   1930                 } else {
   1931                     try {
   1932                         InputStream is = mAssets.openNonAsset(
   1933                                 value.assetCookie, file, AssetManager.ACCESS_STREAMING);
   1934         //                System.out.println("Opened file " + file + ": " + is);
   1935                         dr = Drawable.createFromResourceStream(this, value, is,
   1936                                 file, null);
   1937                         is.close();
   1938         //                System.out.println("Created stream: " + dr);
   1939                     } catch (Exception e) {
   1940                         NotFoundException rnf = new NotFoundException(
   1941                             "File " + file + " from drawable resource ID #0x"
   1942                             + Integer.toHexString(id));
   1943                         rnf.initCause(e);
   1944                         throw rnf;
   1945                     }
   1946                 }
   1947             }
   1948         }
   1949 
   1950         if (dr != null) {
   1951             dr.setChangingConfigurations(value.changingConfigurations);
   1952             cs = dr.getConstantState();
   1953             if (cs != null) {
   1954                 if (mPreloading) {
   1955                     if (isColorDrawable) {
   1956                         sPreloadedColorDrawables.put(key, cs);
   1957                     } else {
   1958                         sPreloadedDrawables.put(key, cs);
   1959                     }
   1960                 } else {
   1961                     synchronized (mTmpValue) {
   1962                         //Log.i(TAG, "Saving cached drawable @ #" +
   1963                         //        Integer.toHexString(key.intValue())
   1964                         //        + " in " + this + ": " + cs);
   1965                         if (isColorDrawable) {
   1966                             mColorDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs));
   1967                         } else {
   1968                             mDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs));
   1969                         }
   1970                     }
   1971                 }
   1972             }
   1973         }
   1974 
   1975         return dr;
   1976     }
   1977 
   1978     private Drawable getCachedDrawable(
   1979             LongSparseArray<WeakReference<ConstantState>> drawableCache,
   1980             long key) {
   1981         synchronized (mTmpValue) {
   1982             WeakReference<Drawable.ConstantState> wr = drawableCache.get(key);
   1983             if (wr != null) {   // we have the key
   1984                 Drawable.ConstantState entry = wr.get();
   1985                 if (entry != null) {
   1986                     //Log.i(TAG, "Returning cached drawable @ #" +
   1987                     //        Integer.toHexString(((Integer)key).intValue())
   1988                     //        + " in " + this + ": " + entry);
   1989                     return entry.newDrawable(this);
   1990                 }
   1991                 else {  // our entry has been purged
   1992                     drawableCache.delete(key);
   1993                 }
   1994             }
   1995         }
   1996         return null;
   1997     }
   1998 
   1999     /*package*/ ColorStateList loadColorStateList(TypedValue value, int id)
   2000             throws NotFoundException {
   2001         if (TRACE_FOR_PRELOAD) {
   2002             // Log only framework resources
   2003             if ((id >>> 24) == 0x1) {
   2004                 final String name = getResourceName(id);
   2005                 if (name != null) android.util.Log.d("PreloadColorStateList", name);
   2006             }
   2007         }
   2008 
   2009         final int key = (value.assetCookie << 24) | value.data;
   2010 
   2011         ColorStateList csl;
   2012 
   2013         if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
   2014                 value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
   2015 
   2016             csl = mPreloadedColorStateLists.get(key);
   2017             if (csl != null) {
   2018                 return csl;
   2019             }
   2020 
   2021             csl = ColorStateList.valueOf(value.data);
   2022             if (mPreloading) {
   2023                 mPreloadedColorStateLists.put(key, csl);
   2024             }
   2025 
   2026             return csl;
   2027         }
   2028 
   2029         csl = getCachedColorStateList(key);
   2030         if (csl != null) {
   2031             return csl;
   2032         }
   2033 
   2034         csl = mPreloadedColorStateLists.get(key);
   2035         if (csl != null) {
   2036             return csl;
   2037         }
   2038 
   2039         if (value.string == null) {
   2040             throw new NotFoundException(
   2041                     "Resource is not a ColorStateList (color or path): " + value);
   2042         }
   2043 
   2044         String file = value.string.toString();
   2045 
   2046         if (file.endsWith(".xml")) {
   2047             try {
   2048                 XmlResourceParser rp = loadXmlResourceParser(
   2049                         file, id, value.assetCookie, "colorstatelist");
   2050                 csl = ColorStateList.createFromXml(this, rp);
   2051                 rp.close();
   2052             } catch (Exception e) {
   2053                 NotFoundException rnf = new NotFoundException(
   2054                     "File " + file + " from color state list resource ID #0x"
   2055                     + Integer.toHexString(id));
   2056                 rnf.initCause(e);
   2057                 throw rnf;
   2058             }
   2059         } else {
   2060             throw new NotFoundException(
   2061                     "File " + file + " from drawable resource ID #0x"
   2062                     + Integer.toHexString(id) + ": .xml extension required");
   2063         }
   2064 
   2065         if (csl != null) {
   2066             if (mPreloading) {
   2067                 mPreloadedColorStateLists.put(key, csl);
   2068             } else {
   2069                 synchronized (mTmpValue) {
   2070                     //Log.i(TAG, "Saving cached color state list @ #" +
   2071                     //        Integer.toHexString(key.intValue())
   2072                     //        + " in " + this + ": " + csl);
   2073                     mColorStateListCache.put(
   2074                         key, new WeakReference<ColorStateList>(csl));
   2075                 }
   2076             }
   2077         }
   2078 
   2079         return csl;
   2080     }
   2081 
   2082     private ColorStateList getCachedColorStateList(int key) {
   2083         synchronized (mTmpValue) {
   2084             WeakReference<ColorStateList> wr = mColorStateListCache.get(key);
   2085             if (wr != null) {   // we have the key
   2086                 ColorStateList entry = wr.get();
   2087                 if (entry != null) {
   2088                     //Log.i(TAG, "Returning cached color state list @ #" +
   2089                     //        Integer.toHexString(((Integer)key).intValue())
   2090                     //        + " in " + this + ": " + entry);
   2091                     return entry;
   2092                 }
   2093                 else {  // our entry has been purged
   2094                     mColorStateListCache.delete(key);
   2095                 }
   2096             }
   2097         }
   2098         return null;
   2099     }
   2100 
   2101     /*package*/ XmlResourceParser loadXmlResourceParser(int id, String type)
   2102             throws NotFoundException {
   2103         synchronized (mTmpValue) {
   2104             TypedValue value = mTmpValue;
   2105             getValue(id, value, true);
   2106             if (value.type == TypedValue.TYPE_STRING) {
   2107                 return loadXmlResourceParser(value.string.toString(), id,
   2108                         value.assetCookie, type);
   2109             }
   2110             throw new NotFoundException(
   2111                     "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
   2112                     + Integer.toHexString(value.type) + " is not valid");
   2113         }
   2114     }
   2115 
   2116     /*package*/ XmlResourceParser loadXmlResourceParser(String file, int id,
   2117             int assetCookie, String type) throws NotFoundException {
   2118         if (id != 0) {
   2119             try {
   2120                 // These may be compiled...
   2121                 synchronized (mCachedXmlBlockIds) {
   2122                     // First see if this block is in our cache.
   2123                     final int num = mCachedXmlBlockIds.length;
   2124                     for (int i=0; i<num; i++) {
   2125                         if (mCachedXmlBlockIds[i] == id) {
   2126                             //System.out.println("**** REUSING XML BLOCK!  id="
   2127                             //                   + id + ", index=" + i);
   2128                             return mCachedXmlBlocks[i].newParser();
   2129                         }
   2130                     }
   2131 
   2132                     // Not in the cache, create a new block and put it at
   2133                     // the next slot in the cache.
   2134                     XmlBlock block = mAssets.openXmlBlockAsset(
   2135                             assetCookie, file);
   2136                     if (block != null) {
   2137                         int pos = mLastCachedXmlBlockIndex+1;
   2138                         if (pos >= num) pos = 0;
   2139                         mLastCachedXmlBlockIndex = pos;
   2140                         XmlBlock oldBlock = mCachedXmlBlocks[pos];
   2141                         if (oldBlock != null) {
   2142                             oldBlock.close();
   2143                         }
   2144                         mCachedXmlBlockIds[pos] = id;
   2145                         mCachedXmlBlocks[pos] = block;
   2146                         //System.out.println("**** CACHING NEW XML BLOCK!  id="
   2147                         //                   + id + ", index=" + pos);
   2148                         return block.newParser();
   2149                     }
   2150                 }
   2151             } catch (Exception e) {
   2152                 NotFoundException rnf = new NotFoundException(
   2153                         "File " + file + " from xml type " + type + " resource ID #0x"
   2154                         + Integer.toHexString(id));
   2155                 rnf.initCause(e);
   2156                 throw rnf;
   2157             }
   2158         }
   2159 
   2160         throw new NotFoundException(
   2161                 "File " + file + " from xml type " + type + " resource ID #0x"
   2162                 + Integer.toHexString(id));
   2163     }
   2164 
   2165     private TypedArray getCachedStyledAttributes(int len) {
   2166         synchronized (mTmpValue) {
   2167             TypedArray attrs = mCachedStyledAttributes;
   2168             if (attrs != null) {
   2169                 mCachedStyledAttributes = null;
   2170                 if (DEBUG_ATTRIBUTES_CACHE) {
   2171                     mLastRetrievedAttrs = new RuntimeException("here");
   2172                     mLastRetrievedAttrs.fillInStackTrace();
   2173                 }
   2174 
   2175                 attrs.mLength = len;
   2176                 int fullLen = len * AssetManager.STYLE_NUM_ENTRIES;
   2177                 if (attrs.mData.length >= fullLen) {
   2178                     return attrs;
   2179                 }
   2180                 attrs.mData = new int[fullLen];
   2181                 attrs.mIndices = new int[1+len];
   2182                 return attrs;
   2183             }
   2184             if (DEBUG_ATTRIBUTES_CACHE) {
   2185                 RuntimeException here = new RuntimeException("here");
   2186                 here.fillInStackTrace();
   2187                 if (mLastRetrievedAttrs != null) {
   2188                     Log.i(TAG, "Allocated new TypedArray of " + len + " in " + this, here);
   2189                     Log.i(TAG, "Last retrieved attributes here", mLastRetrievedAttrs);
   2190                 }
   2191                 mLastRetrievedAttrs = here;
   2192             }
   2193             return new TypedArray(this,
   2194                     new int[len*AssetManager.STYLE_NUM_ENTRIES],
   2195                     new int[1+len], len);
   2196         }
   2197     }
   2198 
   2199     private Resources() {
   2200         mAssets = AssetManager.getSystem();
   2201         // NOTE: Intentionally leaving this uninitialized (all values set
   2202         // to zero), so that anyone who tries to do something that requires
   2203         // metrics will get a very wrong value.
   2204         mConfiguration.setToDefaults();
   2205         mMetrics.setToDefaults();
   2206         updateConfiguration(null, null);
   2207         mAssets.ensureStringBlocks();
   2208         mCompatibilityInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
   2209     }
   2210 }
   2211