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