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