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 android.annotation.AttrRes;
     20 import android.annotation.ColorInt;
     21 import android.annotation.StyleRes;
     22 import android.annotation.StyleableRes;
     23 import com.android.internal.util.GrowingArrayUtils;
     24 import com.android.internal.util.XmlUtils;
     25 
     26 import org.xmlpull.v1.XmlPullParser;
     27 import org.xmlpull.v1.XmlPullParserException;
     28 
     29 import android.animation.Animator;
     30 import android.animation.StateListAnimator;
     31 import android.annotation.AnimRes;
     32 import android.annotation.AnyRes;
     33 import android.annotation.ArrayRes;
     34 import android.annotation.BoolRes;
     35 import android.annotation.ColorRes;
     36 import android.annotation.DimenRes;
     37 import android.annotation.DrawableRes;
     38 import android.annotation.FractionRes;
     39 import android.annotation.IntegerRes;
     40 import android.annotation.LayoutRes;
     41 import android.annotation.NonNull;
     42 import android.annotation.Nullable;
     43 import android.annotation.PluralsRes;
     44 import android.annotation.RawRes;
     45 import android.annotation.StringRes;
     46 import android.annotation.XmlRes;
     47 import android.content.pm.ActivityInfo;
     48 import android.graphics.Movie;
     49 import android.graphics.drawable.ColorDrawable;
     50 import android.graphics.drawable.Drawable;
     51 import android.graphics.drawable.Drawable.ConstantState;
     52 import android.os.Build;
     53 import android.os.Bundle;
     54 import android.os.Trace;
     55 import android.util.ArrayMap;
     56 import android.util.AttributeSet;
     57 import android.util.DisplayMetrics;
     58 import android.util.Log;
     59 import android.util.LongSparseArray;
     60 import android.util.Pools.SynchronizedPool;
     61 import android.util.Slog;
     62 import android.util.TypedValue;
     63 import android.view.ViewDebug;
     64 import android.view.ViewHierarchyEncoder;
     65 
     66 import java.io.IOException;
     67 import java.io.InputStream;
     68 import java.lang.ref.WeakReference;
     69 import java.util.Locale;
     70 
     71 import libcore.icu.NativePluralRules;
     72 
     73 /**
     74  * Class for accessing an application's resources.  This sits on top of the
     75  * asset manager of the application (accessible through {@link #getAssets}) and
     76  * provides a high-level API for getting typed data from the assets.
     77  *
     78  * <p>The Android resource system keeps track of all non-code assets associated with an
     79  * application. You can use this class to access your application's resources. You can generally
     80  * acquire the {@link android.content.res.Resources} instance associated with your application
     81  * with {@link android.content.Context#getResources getResources()}.</p>
     82  *
     83  * <p>The Android SDK tools compile your application's resources into the application binary
     84  * at build time.  To use a resource, you must install it correctly in the source tree (inside
     85  * your project's {@code res/} directory) and build your application.  As part of the build
     86  * process, the SDK tools generate symbols for each resource, which you can use in your application
     87  * code to access the resources.</p>
     88  *
     89  * <p>Using application resources makes it easy to update various characteristics of your
     90  * application without modifying code, and&mdash;by providing sets of alternative
     91  * resources&mdash;enables you to optimize your application for a variety of device configurations
     92  * (such as for different languages and screen sizes). This is an important aspect of developing
     93  * Android applications that are compatible on different types of devices.</p>
     94  *
     95  * <p>For more information about using resources, see the documentation about <a
     96  * href="{@docRoot}guide/topics/resources/index.html">Application Resources</a>.</p>
     97  */
     98 public class Resources {
     99     static final String TAG = "Resources";
    100 
    101     private static final boolean DEBUG_LOAD = false;
    102     private static final boolean DEBUG_CONFIG = false;
    103     private static final boolean TRACE_FOR_PRELOAD = false;
    104     private static final boolean TRACE_FOR_MISS_PRELOAD = false;
    105 
    106     private static final int LAYOUT_DIR_CONFIG = ActivityInfo.activityInfoConfigToNative(
    107             ActivityInfo.CONFIG_LAYOUT_DIRECTION);
    108 
    109     private static final int ID_OTHER = 0x01000004;
    110 
    111     private static final Object sSync = new Object();
    112 
    113     // Information about preloaded resources.  Note that they are not
    114     // protected by a lock, because while preloading in zygote we are all
    115     // single-threaded, and after that these are immutable.
    116     private static final LongSparseArray<ConstantState>[] sPreloadedDrawables;
    117     private static final LongSparseArray<ConstantState> sPreloadedColorDrawables
    118             = new LongSparseArray<>();
    119     private static final LongSparseArray<android.content.res.ConstantState<ColorStateList>>
    120             sPreloadedColorStateLists = new LongSparseArray<>();
    121 
    122     private static final String CACHE_NOT_THEMED = "";
    123     private static final String CACHE_NULL_THEME = "null_theme";
    124 
    125     // Pool of TypedArrays targeted to this Resources object.
    126     final SynchronizedPool<TypedArray> mTypedArrayPool = new SynchronizedPool<>(5);
    127 
    128     // Used by BridgeResources in layoutlib
    129     static Resources mSystem = null;
    130 
    131     private static boolean sPreloaded;
    132     private static int sPreloadedDensity;
    133 
    134     // These are protected by mAccessLock.
    135     private final Object mAccessLock = new Object();
    136     private final Configuration mTmpConfig = new Configuration();
    137     private final DrawableCache mDrawableCache = new DrawableCache(this);
    138     private final DrawableCache mColorDrawableCache = new DrawableCache(this);
    139     private final ConfigurationBoundResourceCache<ColorStateList> mColorStateListCache =
    140             new ConfigurationBoundResourceCache<>(this);
    141     private final ConfigurationBoundResourceCache<Animator> mAnimatorCache =
    142             new ConfigurationBoundResourceCache<>(this);
    143     private final ConfigurationBoundResourceCache<StateListAnimator> mStateListAnimatorCache =
    144             new ConfigurationBoundResourceCache<>(this);
    145 
    146     private TypedValue mTmpValue = new TypedValue();
    147     private boolean mPreloading;
    148 
    149     private int mLastCachedXmlBlockIndex = -1;
    150     private final int[] mCachedXmlBlockIds = { 0, 0, 0, 0 };
    151     private final XmlBlock[] mCachedXmlBlocks = new XmlBlock[4];
    152 
    153     final AssetManager mAssets;
    154     final DisplayMetrics mMetrics = new DisplayMetrics();
    155 
    156     private final Configuration mConfiguration = new Configuration();
    157     private NativePluralRules mPluralRule;
    158 
    159     private CompatibilityInfo mCompatibilityInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
    160 
    161     static {
    162         sPreloadedDrawables = new LongSparseArray[2];
    163         sPreloadedDrawables[0] = new LongSparseArray<>();
    164         sPreloadedDrawables[1] = new LongSparseArray<>();
    165     }
    166 
    167     /**
    168      * Returns the most appropriate default theme for the specified target SDK version.
    169      * <ul>
    170      * <li>Below API 11: Gingerbread
    171      * <li>APIs 11 thru 14: Holo
    172      * <li>APIs 14 thru XX: Device default dark
    173      * <li>API XX and above: Device default light with dark action bar
    174      * </ul>
    175      *
    176      * @param curTheme The current theme, or 0 if not specified.
    177      * @param targetSdkVersion The target SDK version.
    178      * @return A theme resource identifier
    179      * @hide
    180      */
    181     public static int selectDefaultTheme(int curTheme, int targetSdkVersion) {
    182         return selectSystemTheme(curTheme, targetSdkVersion,
    183                 com.android.internal.R.style.Theme,
    184                 com.android.internal.R.style.Theme_Holo,
    185                 com.android.internal.R.style.Theme_DeviceDefault,
    186                 com.android.internal.R.style.Theme_DeviceDefault_Light_DarkActionBar);
    187     }
    188 
    189     /** @hide */
    190     public static int selectSystemTheme(int curTheme, int targetSdkVersion, int orig, int holo,
    191             int dark, int deviceDefault) {
    192         if (curTheme != 0) {
    193             return curTheme;
    194         }
    195         if (targetSdkVersion < Build.VERSION_CODES.HONEYCOMB) {
    196             return orig;
    197         }
    198         if (targetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
    199             return holo;
    200         }
    201         if (targetSdkVersion < Build.VERSION_CODES.CUR_DEVELOPMENT) {
    202             return dark;
    203         }
    204         return deviceDefault;
    205     }
    206 
    207     /**
    208      * Used by AnimatorInflater.
    209      *
    210      * @hide
    211      */
    212     public ConfigurationBoundResourceCache<Animator> getAnimatorCache() {
    213         return mAnimatorCache;
    214     }
    215 
    216     /**
    217      * Used by AnimatorInflater.
    218      *
    219      * @hide
    220      */
    221     public ConfigurationBoundResourceCache<StateListAnimator> getStateListAnimatorCache() {
    222         return mStateListAnimatorCache;
    223     }
    224 
    225     /**
    226      * This exception is thrown by the resource APIs when a requested resource
    227      * can not be found.
    228      */
    229     public static class NotFoundException extends RuntimeException {
    230         public NotFoundException() {
    231         }
    232 
    233         public NotFoundException(String name) {
    234             super(name);
    235         }
    236     }
    237 
    238     /**
    239      * Create a new Resources object on top of an existing set of assets in an
    240      * AssetManager.
    241      *
    242      * @param assets Previously created AssetManager.
    243      * @param metrics Current display metrics to consider when
    244      *                selecting/computing resource values.
    245      * @param config Desired device configuration to consider when
    246      *               selecting/computing resource values (optional).
    247      */
    248     public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) {
    249         this(assets, metrics, config, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO);
    250     }
    251 
    252     /**
    253      * Creates a new Resources object with CompatibilityInfo.
    254      *
    255      * @param assets Previously created AssetManager.
    256      * @param metrics Current display metrics to consider when
    257      *                selecting/computing resource values.
    258      * @param config Desired device configuration to consider when
    259      *               selecting/computing resource values (optional).
    260      * @param compatInfo this resource's compatibility info. Must not be null.
    261      * @hide
    262      */
    263     public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config,
    264             CompatibilityInfo compatInfo) {
    265         mAssets = assets;
    266         mMetrics.setToDefaults();
    267         if (compatInfo != null) {
    268             mCompatibilityInfo = compatInfo;
    269         }
    270         updateConfiguration(config, metrics);
    271         assets.ensureStringBlocks();
    272     }
    273 
    274     /**
    275      * Return a global shared Resources object that provides access to only
    276      * system resources (no application resources), and is not configured for
    277      * the current screen (can not use dimension units, does not change based
    278      * on orientation, etc).
    279      */
    280     public static Resources getSystem() {
    281         synchronized (sSync) {
    282             Resources ret = mSystem;
    283             if (ret == null) {
    284                 ret = new Resources();
    285                 mSystem = ret;
    286             }
    287 
    288             return ret;
    289         }
    290     }
    291 
    292     /**
    293      * Return the string value associated with a particular resource ID.  The
    294      * returned object will be a String if this is a plain string; it will be
    295      * some other type of CharSequence if it is styled.
    296      * {@more}
    297      *
    298      * @param id The desired resource identifier, as generated by the aapt
    299      *           tool. This integer encodes the package, type, and resource
    300      *           entry. The value 0 is an invalid identifier.
    301      *
    302      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
    303      *
    304      * @return CharSequence The string data associated with the resource, plus
    305      *         possibly styled text information.
    306      */
    307     public CharSequence getText(@StringRes int id) throws NotFoundException {
    308         CharSequence res = mAssets.getResourceText(id);
    309         if (res != null) {
    310             return res;
    311         }
    312         throw new NotFoundException("String resource ID #0x"
    313                                     + Integer.toHexString(id));
    314     }
    315 
    316     /**
    317      * Returns the character sequence necessary for grammatically correct pluralization
    318      * of the given resource ID for the given quantity.
    319      * Note that the character sequence is selected based solely on grammatical necessity,
    320      * and that such rules differ between languages. Do not assume you know which string
    321      * will be returned for a given quantity. See
    322      * <a href="{@docRoot}guide/topics/resources/string-resource.html#Plurals">String Resources</a>
    323      * for more detail.
    324      *
    325      * @param id The desired resource identifier, as generated by the aapt
    326      *           tool. This integer encodes the package, type, and resource
    327      *           entry. The value 0 is an invalid identifier.
    328      * @param quantity The number used to get the correct string for the current language's
    329      *           plural rules.
    330      *
    331      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
    332      *
    333      * @return CharSequence The string data associated with the resource, plus
    334      *         possibly styled text information.
    335      */
    336     public CharSequence getQuantityText(@PluralsRes int id, int quantity)
    337             throws NotFoundException {
    338         NativePluralRules rule = getPluralRule();
    339         CharSequence res = mAssets.getResourceBagText(id,
    340                 attrForQuantityCode(rule.quantityForInt(quantity)));
    341         if (res != null) {
    342             return res;
    343         }
    344         res = mAssets.getResourceBagText(id, ID_OTHER);
    345         if (res != null) {
    346             return res;
    347         }
    348         throw new NotFoundException("Plural resource ID #0x" + Integer.toHexString(id)
    349                 + " quantity=" + quantity
    350                 + " item=" + stringForQuantityCode(rule.quantityForInt(quantity)));
    351     }
    352 
    353     private NativePluralRules getPluralRule() {
    354         synchronized (sSync) {
    355             if (mPluralRule == null) {
    356                 mPluralRule = NativePluralRules.forLocale(mConfiguration.locale);
    357             }
    358             return mPluralRule;
    359         }
    360     }
    361 
    362     private static int attrForQuantityCode(int quantityCode) {
    363         switch (quantityCode) {
    364             case NativePluralRules.ZERO: return 0x01000005;
    365             case NativePluralRules.ONE:  return 0x01000006;
    366             case NativePluralRules.TWO:  return 0x01000007;
    367             case NativePluralRules.FEW:  return 0x01000008;
    368             case NativePluralRules.MANY: return 0x01000009;
    369             default:                     return ID_OTHER;
    370         }
    371     }
    372 
    373     private static String stringForQuantityCode(int quantityCode) {
    374         switch (quantityCode) {
    375             case NativePluralRules.ZERO: return "zero";
    376             case NativePluralRules.ONE:  return "one";
    377             case NativePluralRules.TWO:  return "two";
    378             case NativePluralRules.FEW:  return "few";
    379             case NativePluralRules.MANY: return "many";
    380             default:                     return "other";
    381         }
    382     }
    383 
    384     /**
    385      * Return the string value associated with a particular resource ID.  It
    386      * will be stripped of any styled text information.
    387      * {@more}
    388      *
    389      * @param id The desired resource identifier, as generated by the aapt
    390      *           tool. This integer encodes the package, type, and resource
    391      *           entry. The value 0 is an invalid identifier.
    392      *
    393      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
    394      *
    395      * @return String The string data associated with the resource,
    396      *         stripped of styled text information.
    397      */
    398     @NonNull
    399     public String getString(@StringRes int id) throws NotFoundException {
    400         final CharSequence res = getText(id);
    401         if (res != null) {
    402             return res.toString();
    403         }
    404         throw new NotFoundException("String resource ID #0x"
    405                                     + Integer.toHexString(id));
    406     }
    407 
    408 
    409     /**
    410      * Return the string value associated with a particular resource ID,
    411      * substituting the format arguments as defined in {@link java.util.Formatter}
    412      * and {@link java.lang.String#format}. It will be stripped of any styled text
    413      * information.
    414      * {@more}
    415      *
    416      * @param id The desired resource identifier, as generated by the aapt
    417      *           tool. This integer encodes the package, type, and resource
    418      *           entry. The value 0 is an invalid identifier.
    419      *
    420      * @param formatArgs The format arguments that will be used for substitution.
    421      *
    422      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
    423      *
    424      * @return String The string data associated with the resource,
    425      *         stripped of styled text information.
    426      */
    427     @NonNull
    428     public String getString(@StringRes int id, Object... formatArgs) throws NotFoundException {
    429         final String raw = getString(id);
    430         return String.format(mConfiguration.locale, raw, formatArgs);
    431     }
    432 
    433     /**
    434      * Formats the string necessary for grammatically correct pluralization
    435      * of the given resource ID for the given quantity, using the given arguments.
    436      * Note that the string is selected based solely on grammatical necessity,
    437      * and that such rules differ between languages. Do not assume you know which string
    438      * will be returned for a given quantity. See
    439      * <a href="{@docRoot}guide/topics/resources/string-resource.html#Plurals">String Resources</a>
    440      * for more detail.
    441      *
    442      * <p>Substitution of format arguments works as if using
    443      * {@link java.util.Formatter} and {@link java.lang.String#format}.
    444      * The resulting string will be stripped of any styled text information.
    445      *
    446      * @param id The desired resource identifier, as generated by the aapt
    447      *           tool. This integer encodes the package, type, and resource
    448      *           entry. The value 0 is an invalid identifier.
    449      * @param quantity The number used to get the correct string for the current language's
    450      *           plural rules.
    451      * @param formatArgs The format arguments that will be used for substitution.
    452      *
    453      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
    454      *
    455      * @return String The string data associated with the resource,
    456      * stripped of styled text information.
    457      */
    458     public String getQuantityString(@PluralsRes int id, int quantity, Object... formatArgs)
    459             throws NotFoundException {
    460         String raw = getQuantityText(id, quantity).toString();
    461         return String.format(mConfiguration.locale, raw, formatArgs);
    462     }
    463 
    464     /**
    465      * Returns the string necessary for grammatically correct pluralization
    466      * of the given resource ID for the given quantity.
    467      * Note that the string is selected based solely on grammatical necessity,
    468      * and that such rules differ between languages. Do not assume you know which string
    469      * will be returned for a given quantity. See
    470      * <a href="{@docRoot}guide/topics/resources/string-resource.html#Plurals">String Resources</a>
    471      * for more detail.
    472      *
    473      * @param id The desired resource identifier, as generated by the aapt
    474      *           tool. This integer encodes the package, type, and resource
    475      *           entry. The value 0 is an invalid identifier.
    476      * @param quantity The number used to get the correct string for the current language's
    477      *           plural rules.
    478      *
    479      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
    480      *
    481      * @return String The string data associated with the resource,
    482      * stripped of styled text information.
    483      */
    484     public String getQuantityString(@PluralsRes int id, int quantity)
    485             throws NotFoundException {
    486         return getQuantityText(id, quantity).toString();
    487     }
    488 
    489     /**
    490      * Return the string value associated with a particular resource ID.  The
    491      * returned object will be a String if this is a plain string; it will be
    492      * some other type of CharSequence if it is styled.
    493      *
    494      * @param id The desired resource identifier, as generated by the aapt
    495      *           tool. This integer encodes the package, type, and resource
    496      *           entry. The value 0 is an invalid identifier.
    497      *
    498      * @param def The default CharSequence to return.
    499      *
    500      * @return CharSequence The string data associated with the resource, plus
    501      *         possibly styled text information, or def if id is 0 or not found.
    502      */
    503     public CharSequence getText(@StringRes int id, CharSequence def) {
    504         CharSequence res = id != 0 ? mAssets.getResourceText(id) : null;
    505         return res != null ? res : def;
    506     }
    507 
    508     /**
    509      * Return the styled text array associated with a particular resource ID.
    510      *
    511      * @param id The desired resource identifier, as generated by the aapt
    512      *           tool. This integer encodes the package, type, and resource
    513      *           entry. The value 0 is an invalid identifier.
    514      *
    515      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
    516      *
    517      * @return The styled text array associated with the resource.
    518      */
    519     public CharSequence[] getTextArray(@ArrayRes int id) throws NotFoundException {
    520         CharSequence[] res = mAssets.getResourceTextArray(id);
    521         if (res != null) {
    522             return res;
    523         }
    524         throw new NotFoundException("Text array resource ID #0x"
    525                                     + Integer.toHexString(id));
    526     }
    527 
    528     /**
    529      * Return the string array associated with a particular resource ID.
    530      *
    531      * @param id The desired resource identifier, as generated by the aapt
    532      *           tool. This integer encodes the package, type, and resource
    533      *           entry. The value 0 is an invalid identifier.
    534      *
    535      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
    536      *
    537      * @return The string array associated with the resource.
    538      */
    539     public String[] getStringArray(@ArrayRes int id)
    540             throws NotFoundException {
    541         String[] res = mAssets.getResourceStringArray(id);
    542         if (res != null) {
    543             return res;
    544         }
    545         throw new NotFoundException("String array resource ID #0x"
    546                                     + Integer.toHexString(id));
    547     }
    548 
    549     /**
    550      * Return the int array associated with a particular resource ID.
    551      *
    552      * @param id The desired resource identifier, as generated by the aapt
    553      *           tool. This integer encodes the package, type, and resource
    554      *           entry. The value 0 is an invalid identifier.
    555      *
    556      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
    557      *
    558      * @return The int array associated with the resource.
    559      */
    560     public int[] getIntArray(@ArrayRes int id) throws NotFoundException {
    561         int[] res = mAssets.getArrayIntResource(id);
    562         if (res != null) {
    563             return res;
    564         }
    565         throw new NotFoundException("Int array resource ID #0x"
    566                                     + Integer.toHexString(id));
    567     }
    568 
    569     /**
    570      * Return an array of heterogeneous values.
    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      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
    577      *
    578      * @return Returns a TypedArray holding an array of the array values.
    579      * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
    580      * when done with it.
    581      */
    582     public TypedArray obtainTypedArray(@ArrayRes int id)
    583             throws NotFoundException {
    584         int len = mAssets.getArraySize(id);
    585         if (len < 0) {
    586             throw new NotFoundException("Array resource ID #0x"
    587                                         + Integer.toHexString(id));
    588         }
    589 
    590         TypedArray array = TypedArray.obtain(this, len);
    591         array.mLength = mAssets.retrieveArray(id, array.mData);
    592         array.mIndices[0] = 0;
    593 
    594         return array;
    595     }
    596 
    597     /**
    598      * Retrieve a dimensional for a particular resource ID.  Unit
    599      * conversions are based on the current {@link DisplayMetrics} associated
    600      * with the resources.
    601      *
    602      * @param id The desired resource identifier, as generated by the aapt
    603      *           tool. This integer encodes the package, type, and resource
    604      *           entry. The value 0 is an invalid identifier.
    605      *
    606      * @return Resource dimension value multiplied by the appropriate
    607      * metric.
    608      *
    609      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
    610      *
    611      * @see #getDimensionPixelOffset
    612      * @see #getDimensionPixelSize
    613      */
    614     public float getDimension(@DimenRes int id) throws NotFoundException {
    615         synchronized (mAccessLock) {
    616             TypedValue value = mTmpValue;
    617             if (value == null) {
    618                 mTmpValue = value = new TypedValue();
    619             }
    620             getValue(id, value, true);
    621             if (value.type == TypedValue.TYPE_DIMENSION) {
    622                 return TypedValue.complexToDimension(value.data, mMetrics);
    623             }
    624             throw new NotFoundException(
    625                     "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
    626                     + Integer.toHexString(value.type) + " is not valid");
    627         }
    628     }
    629 
    630     /**
    631      * Retrieve a dimensional for a particular resource ID for use
    632      * as an offset in raw pixels.  This is the same as
    633      * {@link #getDimension}, except the returned value is converted to
    634      * integer pixels for you.  An offset conversion involves simply
    635      * truncating the base value to an integer.
    636      *
    637      * @param id The desired resource identifier, as generated by the aapt
    638      *           tool. This integer encodes the package, type, and resource
    639      *           entry. The value 0 is an invalid identifier.
    640      *
    641      * @return Resource dimension value multiplied by the appropriate
    642      * metric and truncated to integer pixels.
    643      *
    644      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
    645      *
    646      * @see #getDimension
    647      * @see #getDimensionPixelSize
    648      */
    649     public int getDimensionPixelOffset(@DimenRes int id) throws NotFoundException {
    650         synchronized (mAccessLock) {
    651             TypedValue value = mTmpValue;
    652             if (value == null) {
    653                 mTmpValue = value = new TypedValue();
    654             }
    655             getValue(id, value, true);
    656             if (value.type == TypedValue.TYPE_DIMENSION) {
    657                 return TypedValue.complexToDimensionPixelOffset(
    658                         value.data, mMetrics);
    659             }
    660             throw new NotFoundException(
    661                     "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
    662                     + Integer.toHexString(value.type) + " is not valid");
    663         }
    664     }
    665 
    666     /**
    667      * Retrieve a dimensional for a particular resource ID for use
    668      * as a size in raw pixels.  This is the same as
    669      * {@link #getDimension}, except the returned value is converted to
    670      * integer pixels for use as a size.  A size conversion involves
    671      * rounding the base value, and ensuring that a non-zero base value
    672      * is at least one pixel in size.
    673      *
    674      * @param id The desired resource identifier, as generated by the aapt
    675      *           tool. This integer encodes the package, type, and resource
    676      *           entry. The value 0 is an invalid identifier.
    677      *
    678      * @return Resource dimension value multiplied by the appropriate
    679      * metric and truncated to integer pixels.
    680      *
    681      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
    682      *
    683      * @see #getDimension
    684      * @see #getDimensionPixelOffset
    685      */
    686     public int getDimensionPixelSize(@DimenRes int id) throws NotFoundException {
    687         synchronized (mAccessLock) {
    688             TypedValue value = mTmpValue;
    689             if (value == null) {
    690                 mTmpValue = value = new TypedValue();
    691             }
    692             getValue(id, value, true);
    693             if (value.type == TypedValue.TYPE_DIMENSION) {
    694                 return TypedValue.complexToDimensionPixelSize(
    695                         value.data, mMetrics);
    696             }
    697             throw new NotFoundException(
    698                     "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
    699                     + Integer.toHexString(value.type) + " is not valid");
    700         }
    701     }
    702 
    703     /**
    704      * Retrieve a fractional unit for a particular resource ID.
    705      *
    706      * @param id The desired resource identifier, as generated by the aapt
    707      *           tool. This integer encodes the package, type, and resource
    708      *           entry. The value 0 is an invalid identifier.
    709      * @param base The base value of this fraction.  In other words, a
    710      *             standard fraction is multiplied by this value.
    711      * @param pbase The parent base value of this fraction.  In other
    712      *             words, a parent fraction (nn%p) is multiplied by this
    713      *             value.
    714      *
    715      * @return Attribute fractional value multiplied by the appropriate
    716      * base value.
    717      *
    718      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
    719      */
    720     public float getFraction(@FractionRes int id, int base, int pbase) {
    721         synchronized (mAccessLock) {
    722             TypedValue value = mTmpValue;
    723             if (value == null) {
    724                 mTmpValue = value = new TypedValue();
    725             }
    726             getValue(id, value, true);
    727             if (value.type == TypedValue.TYPE_FRACTION) {
    728                 return TypedValue.complexToFraction(value.data, base, pbase);
    729             }
    730             throw new NotFoundException(
    731                     "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
    732                     + Integer.toHexString(value.type) + " is not valid");
    733         }
    734     }
    735 
    736     /**
    737      * Return a drawable object associated with a particular resource ID.
    738      * Various types of objects will be returned depending on the underlying
    739      * resource -- for example, a solid color, PNG image, scalable image, etc.
    740      * The Drawable API hides these implementation details.
    741      *
    742      * <p class="note"><strong>Note:</strong> Prior to
    743      * {@link android.os.Build.VERSION_CODES#JELLY_BEAN}, this function
    744      * would not correctly retrieve the final configuration density when
    745      * the resource ID passed here is an alias to another Drawable resource.
    746      * This means that if the density configuration of the alias resource
    747      * is different than the actual resource, the density of the returned
    748      * Drawable would be incorrect, resulting in bad scaling.  To work
    749      * around this, you can instead retrieve the Drawable through
    750      * {@link TypedArray#getDrawable TypedArray.getDrawable}.  Use
    751      * {@link android.content.Context#obtainStyledAttributes(int[])
    752      * Context.obtainStyledAttributes} with
    753      * an array containing the resource ID of interest to create the TypedArray.</p>
    754      *
    755      * <p class="note"><strong>Note:</strong> To obtain a themed drawable, use
    756      * {@link android.content.Context#getDrawable(int) Context.getDrawable(int)}
    757      * or {@link #getDrawable(int, Theme)} passing the desired theme.</p>
    758      *
    759      * @param id The desired resource identifier, as generated by the aapt
    760      *           tool. This integer encodes the package, type, and resource
    761      *           entry. The value 0 is an invalid identifier.
    762      * @return Drawable An object that can be used to draw this resource.
    763      * @throws NotFoundException Throws NotFoundException if the given ID does
    764      *         not exist.
    765      * @see #getDrawable(int, Theme)
    766      * @deprecated Use {@link #getDrawable(int, Theme)} instead.
    767      */
    768     @Deprecated
    769     @Nullable
    770     public Drawable getDrawable(@DrawableRes int id) throws NotFoundException {
    771         final Drawable d = getDrawable(id, null);
    772         if (d != null && d.canApplyTheme()) {
    773             Log.w(TAG, "Drawable " + getResourceName(id) + " has unresolved theme "
    774                     + "attributes! Consider using Resources.getDrawable(int, Theme) or "
    775                     + "Context.getDrawable(int).", new RuntimeException());
    776         }
    777         return d;
    778     }
    779 
    780     /**
    781      * Return a drawable object associated with a particular resource ID and
    782      * styled for the specified theme. Various types of objects will be
    783      * returned depending on the underlying resource -- for example, a solid
    784      * color, PNG image, scalable image, etc.
    785      *
    786      * @param id The desired resource identifier, as generated by the aapt
    787      *           tool. This integer encodes the package, type, and resource
    788      *           entry. The value 0 is an invalid identifier.
    789      * @param theme The theme used to style the drawable attributes, may be {@code null}.
    790      * @return Drawable An object that can be used to draw this resource.
    791      * @throws NotFoundException Throws NotFoundException if the given ID does
    792      *         not exist.
    793      */
    794     @Nullable
    795     public Drawable getDrawable(@DrawableRes int id, @Nullable Theme theme) throws NotFoundException {
    796         TypedValue value;
    797         synchronized (mAccessLock) {
    798             value = mTmpValue;
    799             if (value == null) {
    800                 value = new TypedValue();
    801             } else {
    802                 mTmpValue = null;
    803             }
    804             getValue(id, value, true);
    805         }
    806         final Drawable res = loadDrawable(value, id, theme);
    807         synchronized (mAccessLock) {
    808             if (mTmpValue == null) {
    809                 mTmpValue = value;
    810             }
    811         }
    812         return res;
    813     }
    814 
    815     /**
    816      * Return a drawable object associated with a particular resource ID for the
    817      * given screen density in DPI. This will set the drawable's density to be
    818      * the device's density multiplied by the ratio of actual drawable density
    819      * to requested density. This allows the drawable to be scaled up to the
    820      * correct size if needed. Various types of objects will be returned
    821      * depending on the underlying resource -- for example, a solid color, PNG
    822      * image, scalable image, etc. The Drawable API hides these implementation
    823      * details.
    824      *
    825      * <p class="note"><strong>Note:</strong> To obtain a themed drawable, use
    826      * {@link android.content.Context#getDrawable(int) Context.getDrawable(int)}
    827      * or {@link #getDrawableForDensity(int, int, Theme)} passing the desired
    828      * theme.</p>
    829      *
    830      * @param id The desired resource identifier, as generated by the aapt tool.
    831      *            This integer encodes the package, type, and resource entry.
    832      *            The value 0 is an invalid identifier.
    833      * @param density the desired screen density indicated by the resource as
    834      *            found in {@link DisplayMetrics}.
    835      * @return Drawable An object that can be used to draw this resource.
    836      * @throws NotFoundException Throws NotFoundException if the given ID does
    837      *             not exist.
    838      * @see #getDrawableForDensity(int, int, Theme)
    839      * @deprecated Use {@link #getDrawableForDensity(int, int, Theme)} instead.
    840      */
    841     @Deprecated
    842     @Nullable
    843     public Drawable getDrawableForDensity(@DrawableRes int id, int density) throws NotFoundException {
    844         return getDrawableForDensity(id, density, null);
    845     }
    846 
    847     /**
    848      * Return a drawable object associated with a particular resource ID for the
    849      * given screen density in DPI and styled for the specified theme.
    850      *
    851      * @param id The desired resource identifier, as generated by the aapt tool.
    852      *            This integer encodes the package, type, and resource entry.
    853      *            The value 0 is an invalid identifier.
    854      * @param density The desired screen density indicated by the resource as
    855      *            found in {@link DisplayMetrics}.
    856      * @param theme The theme used to style the drawable attributes, may be {@code null}.
    857      * @return Drawable An object that can be used to draw this resource.
    858      * @throws NotFoundException Throws NotFoundException if the given ID does
    859      *             not exist.
    860      */
    861     @Nullable
    862     public Drawable getDrawableForDensity(@DrawableRes int id, int density, @Nullable Theme theme) {
    863         TypedValue value;
    864         synchronized (mAccessLock) {
    865             value = mTmpValue;
    866             if (value == null) {
    867                 value = new TypedValue();
    868             } else {
    869                 mTmpValue = null;
    870             }
    871             getValueForDensity(id, density, value, true);
    872 
    873             /*
    874              * Pretend the requested density is actually the display density. If
    875              * the drawable returned is not the requested density, then force it
    876              * to be scaled later by dividing its density by the ratio of
    877              * requested density to actual device density. Drawables that have
    878              * undefined density or no density don't need to be handled here.
    879              */
    880             if (value.density > 0 && value.density != TypedValue.DENSITY_NONE) {
    881                 if (value.density == density) {
    882                     value.density = mMetrics.densityDpi;
    883                 } else {
    884                     value.density = (value.density * mMetrics.densityDpi) / density;
    885                 }
    886             }
    887         }
    888 
    889         final Drawable res = loadDrawable(value, id, theme);
    890         synchronized (mAccessLock) {
    891             if (mTmpValue == null) {
    892                 mTmpValue = value;
    893             }
    894         }
    895         return res;
    896     }
    897 
    898     /**
    899      * Return a movie object associated with the particular resource ID.
    900      * @param id The desired resource identifier, as generated by the aapt
    901      *           tool. This integer encodes the package, type, and resource
    902      *           entry. The value 0 is an invalid identifier.
    903      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
    904      *
    905      */
    906     public Movie getMovie(@RawRes int id) throws NotFoundException {
    907         InputStream is = openRawResource(id);
    908         Movie movie = Movie.decodeStream(is);
    909         try {
    910             is.close();
    911         }
    912         catch (java.io.IOException e) {
    913             // don't care, since the return value is valid
    914         }
    915         return movie;
    916     }
    917 
    918     /**
    919      * Returns a color integer associated with a particular resource ID. If the
    920      * resource holds a complex {@link ColorStateList}, then the default color
    921      * from the set is returned.
    922      *
    923      * @param id The desired resource identifier, as generated by the aapt
    924      *           tool. This integer encodes the package, type, and resource
    925      *           entry. The value 0 is an invalid identifier.
    926      *
    927      * @throws NotFoundException Throws NotFoundException if the given ID does
    928      *         not exist.
    929      *
    930      * @return A single color value in the form 0xAARRGGBB.
    931      * @deprecated Use {@link #getColor(int, Theme)} instead.
    932      */
    933     @ColorInt
    934     @Deprecated
    935     public int getColor(@ColorRes int id) throws NotFoundException {
    936         return getColor(id, null);
    937     }
    938 
    939     /**
    940      * Returns a themed color integer associated with a particular resource ID.
    941      * If the resource holds a complex {@link ColorStateList}, then the default
    942      * color from the set is returned.
    943      *
    944      * @param id The desired resource identifier, as generated by the aapt
    945      *           tool. This integer encodes the package, type, and resource
    946      *           entry. The value 0 is an invalid identifier.
    947      * @param theme The theme used to style the color attributes, may be
    948      *              {@code null}.
    949      *
    950      * @throws NotFoundException Throws NotFoundException if the given ID does
    951      *         not exist.
    952      *
    953      * @return A single color value in the form 0xAARRGGBB.
    954      */
    955     @ColorInt
    956     public int getColor(@ColorRes int id, @Nullable Theme theme) throws NotFoundException {
    957         TypedValue value;
    958         synchronized (mAccessLock) {
    959             value = mTmpValue;
    960             if (value == null) {
    961                 value = new TypedValue();
    962             }
    963             getValue(id, value, true);
    964             if (value.type >= TypedValue.TYPE_FIRST_INT
    965                     && value.type <= TypedValue.TYPE_LAST_INT) {
    966                 mTmpValue = value;
    967                 return value.data;
    968             } else if (value.type != TypedValue.TYPE_STRING) {
    969                 throw new NotFoundException(
    970                         "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
    971                                 + Integer.toHexString(value.type) + " is not valid");
    972             }
    973             mTmpValue = null;
    974         }
    975 
    976         final ColorStateList csl = loadColorStateList(value, id, theme);
    977         synchronized (mAccessLock) {
    978             if (mTmpValue == null) {
    979                 mTmpValue = value;
    980             }
    981         }
    982 
    983         return csl.getDefaultColor();
    984     }
    985 
    986     /**
    987      * Returns a color state list associated with a particular resource ID. The
    988      * resource may contain either a single raw color value or a complex
    989      * {@link ColorStateList} holding multiple possible colors.
    990      *
    991      * @param id The desired resource identifier of a {@link ColorStateList},
    992      *           as generated by the aapt tool. This integer encodes the
    993      *           package, type, and resource entry. The value 0 is an invalid
    994      *           identifier.
    995      *
    996      * @throws NotFoundException Throws NotFoundException if the given ID does
    997      *         not exist.
    998      *
    999      * @return A ColorStateList object containing either a single solid color
   1000      *         or multiple colors that can be selected based on a state.
   1001      * @deprecated Use {@link #getColorStateList(int, Theme)} instead.
   1002      */
   1003     @Nullable
   1004     @Deprecated
   1005     public ColorStateList getColorStateList(@ColorRes int id) throws NotFoundException {
   1006         final ColorStateList csl = getColorStateList(id, null);
   1007         if (csl != null && csl.canApplyTheme()) {
   1008             Log.w(TAG, "ColorStateList " + getResourceName(id) + " has "
   1009                     + "unresolved theme attributes! Consider using "
   1010                     + "Resources.getColorStateList(int, Theme) or "
   1011                     + "Context.getColorStateList(int).", new RuntimeException());
   1012         }
   1013         return csl;
   1014     }
   1015 
   1016     /**
   1017      * Returns a themed color state list associated with a particular resource
   1018      * ID. The resource may contain either a single raw color value or a
   1019      * complex {@link ColorStateList} holding multiple possible colors.
   1020      *
   1021      * @param id The desired resource identifier of a {@link ColorStateList},
   1022      *           as generated by the aapt tool. This integer encodes the
   1023      *           package, type, and resource entry. The value 0 is an invalid
   1024      *           identifier.
   1025      * @param theme The theme used to style the color attributes, may be
   1026      *              {@code null}.
   1027      *
   1028      * @throws NotFoundException Throws NotFoundException if the given ID does
   1029      *         not exist.
   1030      *
   1031      * @return A themed ColorStateList object containing either a single solid
   1032      *         color or multiple colors that can be selected based on a state.
   1033      */
   1034     @Nullable
   1035     public ColorStateList getColorStateList(@ColorRes int id, @Nullable Theme theme)
   1036             throws NotFoundException {
   1037         TypedValue value;
   1038         synchronized (mAccessLock) {
   1039             value = mTmpValue;
   1040             if (value == null) {
   1041                 value = new TypedValue();
   1042             } else {
   1043                 mTmpValue = null;
   1044             }
   1045             getValue(id, value, true);
   1046         }
   1047 
   1048         final ColorStateList res = loadColorStateList(value, id, theme);
   1049         synchronized (mAccessLock) {
   1050             if (mTmpValue == null) {
   1051                 mTmpValue = value;
   1052             }
   1053         }
   1054 
   1055         return res;
   1056     }
   1057 
   1058     /**
   1059      * Return a boolean associated with a particular resource ID.  This can be
   1060      * used with any integral resource value, and will return true if it is
   1061      * non-zero.
   1062      *
   1063      * @param id The desired resource identifier, as generated by the aapt
   1064      *           tool. This integer encodes the package, type, and resource
   1065      *           entry. The value 0 is an invalid identifier.
   1066      *
   1067      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
   1068      *
   1069      * @return Returns the boolean value contained in the resource.
   1070      */
   1071     public boolean getBoolean(@BoolRes int id) throws NotFoundException {
   1072         synchronized (mAccessLock) {
   1073             TypedValue value = mTmpValue;
   1074             if (value == null) {
   1075                 mTmpValue = value = new TypedValue();
   1076             }
   1077             getValue(id, value, true);
   1078             if (value.type >= TypedValue.TYPE_FIRST_INT
   1079                 && value.type <= TypedValue.TYPE_LAST_INT) {
   1080                 return value.data != 0;
   1081             }
   1082             throw new NotFoundException(
   1083                 "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
   1084                 + Integer.toHexString(value.type) + " is not valid");
   1085         }
   1086     }
   1087 
   1088     /**
   1089      * Return an integer associated with a particular resource ID.
   1090      *
   1091      * @param id The desired resource identifier, as generated by the aapt
   1092      *           tool. This integer encodes the package, type, and resource
   1093      *           entry. The value 0 is an invalid identifier.
   1094      *
   1095      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
   1096      *
   1097      * @return Returns the integer value contained in the resource.
   1098      */
   1099     public int getInteger(@IntegerRes int id) throws NotFoundException {
   1100         synchronized (mAccessLock) {
   1101             TypedValue value = mTmpValue;
   1102             if (value == null) {
   1103                 mTmpValue = value = new TypedValue();
   1104             }
   1105             getValue(id, value, true);
   1106             if (value.type >= TypedValue.TYPE_FIRST_INT
   1107                 && value.type <= TypedValue.TYPE_LAST_INT) {
   1108                 return value.data;
   1109             }
   1110             throw new NotFoundException(
   1111                 "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
   1112                 + Integer.toHexString(value.type) + " is not valid");
   1113         }
   1114     }
   1115 
   1116     /**
   1117      * Retrieve a floating-point value for a particular resource ID.
   1118      *
   1119      * @param id The desired resource identifier, as generated by the aapt
   1120      *           tool. This integer encodes the package, type, and resource
   1121      *           entry. The value 0 is an invalid identifier.
   1122      *
   1123      * @return Returns the floating-point value contained in the resource.
   1124      *
   1125      * @throws NotFoundException Throws NotFoundException if the given ID does
   1126      *         not exist or is not a floating-point value.
   1127      * @hide Pending API council approval.
   1128      */
   1129     public float getFloat(int id) {
   1130         synchronized (mAccessLock) {
   1131             TypedValue value = mTmpValue;
   1132             if (value == null) {
   1133                 mTmpValue = value = new TypedValue();
   1134             }
   1135             getValue(id, value, true);
   1136             if (value.type == TypedValue.TYPE_FLOAT) {
   1137                 return value.getFloat();
   1138             }
   1139             throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id) + " type #0x"
   1140                     + Integer.toHexString(value.type) + " is not valid");
   1141         }
   1142     }
   1143 
   1144     /**
   1145      * Return an XmlResourceParser through which you can read a view layout
   1146      * description for the given resource ID.  This parser has limited
   1147      * functionality -- in particular, you can't change its input, and only
   1148      * the high-level events are available.
   1149      *
   1150      * <p>This function is really a simple wrapper for calling
   1151      * {@link #getXml} with a layout resource.
   1152      *
   1153      * @param id The desired resource identifier, as generated by the aapt
   1154      *           tool. This integer encodes the package, type, and resource
   1155      *           entry. The value 0 is an invalid identifier.
   1156      *
   1157      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
   1158      *
   1159      * @return A new parser object through which you can read
   1160      *         the XML data.
   1161      *
   1162      * @see #getXml
   1163      */
   1164     public XmlResourceParser getLayout(@LayoutRes int id) throws NotFoundException {
   1165         return loadXmlResourceParser(id, "layout");
   1166     }
   1167 
   1168     /**
   1169      * Return an XmlResourceParser through which you can read an animation
   1170      * description for the given resource ID.  This parser has limited
   1171      * functionality -- in particular, you can't change its input, and only
   1172      * the high-level events are available.
   1173      *
   1174      * <p>This function is really a simple wrapper for calling
   1175      * {@link #getXml} with an animation resource.
   1176      *
   1177      * @param id The desired resource identifier, as generated by the aapt
   1178      *           tool. This integer encodes the package, type, and resource
   1179      *           entry. The value 0 is an invalid identifier.
   1180      *
   1181      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
   1182      *
   1183      * @return A new parser object through which you can read
   1184      *         the XML data.
   1185      *
   1186      * @see #getXml
   1187      */
   1188     public XmlResourceParser getAnimation(@AnimRes int id) throws NotFoundException {
   1189         return loadXmlResourceParser(id, "anim");
   1190     }
   1191 
   1192     /**
   1193      * Return an XmlResourceParser through which you can read a generic XML
   1194      * resource for the given resource ID.
   1195      *
   1196      * <p>The XmlPullParser implementation returned here has some limited
   1197      * functionality.  In particular, you can't change its input, and only
   1198      * high-level parsing events are available (since the document was
   1199      * pre-parsed for you at build time, which involved merging text and
   1200      * stripping comments).
   1201      *
   1202      * @param id The desired resource identifier, as generated by the aapt
   1203      *           tool. This integer encodes the package, type, and resource
   1204      *           entry. The value 0 is an invalid identifier.
   1205      *
   1206      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
   1207      *
   1208      * @return A new parser object through which you can read
   1209      *         the XML data.
   1210      *
   1211      * @see android.util.AttributeSet
   1212      */
   1213     public XmlResourceParser getXml(@XmlRes int id) throws NotFoundException {
   1214         return loadXmlResourceParser(id, "xml");
   1215     }
   1216 
   1217     /**
   1218      * Open a data stream for reading a raw resource.  This can only be used
   1219      * with resources whose value is the name of an asset files -- that is, it can be
   1220      * used to open drawable, sound, and raw resources; it will fail on string
   1221      * and color resources.
   1222      *
   1223      * @param id The resource identifier to open, as generated by the appt
   1224      *           tool.
   1225      *
   1226      * @return InputStream Access to the resource data.
   1227      *
   1228      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
   1229      *
   1230      */
   1231     public InputStream openRawResource(@RawRes int id) throws NotFoundException {
   1232         TypedValue value;
   1233         synchronized (mAccessLock) {
   1234             value = mTmpValue;
   1235             if (value == null) {
   1236                 value = new TypedValue();
   1237             } else {
   1238                 mTmpValue = null;
   1239             }
   1240         }
   1241         InputStream res = openRawResource(id, value);
   1242         synchronized (mAccessLock) {
   1243             if (mTmpValue == null) {
   1244                 mTmpValue = value;
   1245             }
   1246         }
   1247         return res;
   1248     }
   1249 
   1250     /**
   1251      * Open a data stream for reading a raw resource.  This can only be used
   1252      * with resources whose value is the name of an asset file -- that is, it can be
   1253      * used to open drawable, sound, and raw resources; it will fail on string
   1254      * and color resources.
   1255      *
   1256      * @param id The resource identifier to open, as generated by the appt tool.
   1257      * @param value The TypedValue object to hold the resource information.
   1258      *
   1259      * @return InputStream Access to the resource data.
   1260      *
   1261      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
   1262      */
   1263     public InputStream openRawResource(@RawRes int id, TypedValue value)
   1264             throws NotFoundException {
   1265         getValue(id, value, true);
   1266 
   1267         try {
   1268             return mAssets.openNonAsset(value.assetCookie, value.string.toString(),
   1269                     AssetManager.ACCESS_STREAMING);
   1270         } catch (Exception e) {
   1271             NotFoundException rnf = new NotFoundException("File " + value.string.toString() +
   1272                     " from drawable resource ID #0x" + Integer.toHexString(id));
   1273             rnf.initCause(e);
   1274             throw rnf;
   1275         }
   1276     }
   1277 
   1278     /**
   1279      * Open a file descriptor for reading a raw resource.  This can only be used
   1280      * with resources whose value is the name of an asset files -- that is, it can be
   1281      * used to open drawable, sound, and raw resources; it will fail on string
   1282      * and color resources.
   1283      *
   1284      * <p>This function only works for resources that are stored in the package
   1285      * as uncompressed data, which typically includes things like mp3 files
   1286      * and png images.
   1287      *
   1288      * @param id The resource identifier to open, as generated by the appt
   1289      *           tool.
   1290      *
   1291      * @return AssetFileDescriptor A new file descriptor you can use to read
   1292      * the resource.  This includes the file descriptor itself, as well as the
   1293      * offset and length of data where the resource appears in the file.  A
   1294      * null is returned if the file exists but is compressed.
   1295      *
   1296      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
   1297      *
   1298      */
   1299     public AssetFileDescriptor openRawResourceFd(@RawRes int id)
   1300             throws NotFoundException {
   1301         TypedValue value;
   1302         synchronized (mAccessLock) {
   1303             value = mTmpValue;
   1304             if (value == null) {
   1305                 value = new TypedValue();
   1306             } else {
   1307                 mTmpValue = null;
   1308             }
   1309             getValue(id, value, true);
   1310         }
   1311         try {
   1312             return mAssets.openNonAssetFd(
   1313                 value.assetCookie, value.string.toString());
   1314         } catch (Exception e) {
   1315             NotFoundException rnf = new NotFoundException(
   1316                 "File " + value.string.toString()
   1317                 + " from drawable resource ID #0x"
   1318                 + Integer.toHexString(id));
   1319             rnf.initCause(e);
   1320             throw rnf;
   1321         } finally {
   1322             synchronized (mAccessLock) {
   1323                 if (mTmpValue == null) {
   1324                     mTmpValue = value;
   1325                 }
   1326             }
   1327         }
   1328     }
   1329 
   1330     /**
   1331      * Return the raw data associated with a particular resource ID.
   1332      *
   1333      * @param id The desired resource identifier, as generated by the aapt
   1334      *           tool. This integer encodes the package, type, and resource
   1335      *           entry. The value 0 is an invalid identifier.
   1336      * @param outValue Object in which to place the resource data.
   1337      * @param resolveRefs If true, a resource that is a reference to another
   1338      *                    resource will be followed so that you receive the
   1339      *                    actual final resource data.  If false, the TypedValue
   1340      *                    will be filled in with the reference itself.
   1341      *
   1342      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
   1343      *
   1344      */
   1345     public void getValue(@AnyRes int id, TypedValue outValue, boolean resolveRefs)
   1346             throws NotFoundException {
   1347         boolean found = mAssets.getResourceValue(id, 0, outValue, resolveRefs);
   1348         if (found) {
   1349             return;
   1350         }
   1351         throw new NotFoundException("Resource ID #0x"
   1352                                     + Integer.toHexString(id));
   1353     }
   1354 
   1355     /**
   1356      * Get the raw value associated with a resource with associated density.
   1357      *
   1358      * @param id resource identifier
   1359      * @param density density in DPI
   1360      * @param resolveRefs If true, a resource that is a reference to another
   1361      *            resource will be followed so that you receive the actual final
   1362      *            resource data. If false, the TypedValue will be filled in with
   1363      *            the reference itself.
   1364      * @throws NotFoundException Throws NotFoundException if the given ID does
   1365      *             not exist.
   1366      * @see #getValue(String, TypedValue, boolean)
   1367      */
   1368     public void getValueForDensity(@AnyRes int id, int density, TypedValue outValue,
   1369             boolean resolveRefs) throws NotFoundException {
   1370         boolean found = mAssets.getResourceValue(id, density, outValue, resolveRefs);
   1371         if (found) {
   1372             return;
   1373         }
   1374         throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id));
   1375     }
   1376 
   1377     /**
   1378      * Return the raw data associated with a particular resource ID.
   1379      * See getIdentifier() for information on how names are mapped to resource
   1380      * IDs, and getString(int) for information on how string resources are
   1381      * retrieved.
   1382      *
   1383      * <p>Note: use of this function is discouraged.  It is much more
   1384      * efficient to retrieve resources by identifier than by name.
   1385      *
   1386      * @param name The name of the desired resource.  This is passed to
   1387      *             getIdentifier() with a default type of "string".
   1388      * @param outValue Object in which to place the resource data.
   1389      * @param resolveRefs If true, a resource that is a reference to another
   1390      *                    resource will be followed so that you receive the
   1391      *                    actual final resource data.  If false, the TypedValue
   1392      *                    will be filled in with the reference itself.
   1393      *
   1394      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
   1395      *
   1396      */
   1397     public void getValue(String name, TypedValue outValue, boolean resolveRefs)
   1398             throws NotFoundException {
   1399         int id = getIdentifier(name, "string", null);
   1400         if (id != 0) {
   1401             getValue(id, outValue, resolveRefs);
   1402             return;
   1403         }
   1404         throw new NotFoundException("String resource name " + name);
   1405     }
   1406 
   1407     /**
   1408      * This class holds the current attribute values for a particular theme.
   1409      * In other words, a Theme is a set of values for resource attributes;
   1410      * these are used in conjunction with {@link TypedArray}
   1411      * to resolve the final value for an attribute.
   1412      *
   1413      * <p>The Theme's attributes come into play in two ways: (1) a styled
   1414      * attribute can explicit reference a value in the theme through the
   1415      * "?themeAttribute" syntax; (2) if no value has been defined for a
   1416      * particular styled attribute, as a last resort we will try to find that
   1417      * attribute's value in the Theme.
   1418      *
   1419      * <p>You will normally use the {@link #obtainStyledAttributes} APIs to
   1420      * retrieve XML attributes with style and theme information applied.
   1421      */
   1422     public final class Theme {
   1423         /**
   1424          * Place new attribute values into the theme.  The style resource
   1425          * specified by <var>resid</var> will be retrieved from this Theme's
   1426          * resources, its values placed into the Theme object.
   1427          *
   1428          * <p>The semantics of this function depends on the <var>force</var>
   1429          * argument:  If false, only values that are not already defined in
   1430          * the theme will be copied from the system resource; otherwise, if
   1431          * any of the style's attributes are already defined in the theme, the
   1432          * current values in the theme will be overwritten.
   1433          *
   1434          * @param resId The resource ID of a style resource from which to
   1435          *              obtain attribute values.
   1436          * @param force If true, values in the style resource will always be
   1437          *              used in the theme; otherwise, they will only be used
   1438          *              if not already defined in the theme.
   1439          */
   1440         public void applyStyle(int resId, boolean force) {
   1441             AssetManager.applyThemeStyle(mTheme, resId, force);
   1442 
   1443             mThemeResId = resId;
   1444             mKey.append(resId, force);
   1445         }
   1446 
   1447         /**
   1448          * Set this theme to hold the same contents as the theme
   1449          * <var>other</var>.  If both of these themes are from the same
   1450          * Resources object, they will be identical after this function
   1451          * returns.  If they are from different Resources, only the resources
   1452          * they have in common will be set in this theme.
   1453          *
   1454          * @param other The existing Theme to copy from.
   1455          */
   1456         public void setTo(Theme other) {
   1457             AssetManager.copyTheme(mTheme, other.mTheme);
   1458 
   1459             mThemeResId = other.mThemeResId;
   1460             mKey.setTo(other.getKey());
   1461         }
   1462 
   1463         /**
   1464          * Return a TypedArray holding the values defined by
   1465          * <var>Theme</var> which are listed in <var>attrs</var>.
   1466          *
   1467          * <p>Be sure to call {@link TypedArray#recycle() TypedArray.recycle()} when you are done
   1468          * with the array.
   1469          *
   1470          * @param attrs The desired attributes.
   1471          *
   1472          * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
   1473          *
   1474          * @return Returns a TypedArray holding an array of the attribute values.
   1475          * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
   1476          * when done with it.
   1477          *
   1478          * @see Resources#obtainAttributes
   1479          * @see #obtainStyledAttributes(int, int[])
   1480          * @see #obtainStyledAttributes(AttributeSet, int[], int, int)
   1481          */
   1482         public TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) {
   1483             final int len = attrs.length;
   1484             final TypedArray array = TypedArray.obtain(Resources.this, len);
   1485             array.mTheme = this;
   1486             AssetManager.applyStyle(mTheme, 0, 0, 0, attrs, array.mData, array.mIndices);
   1487             return array;
   1488         }
   1489 
   1490         /**
   1491          * Return a TypedArray holding the values defined by the style
   1492          * resource <var>resid</var> which are listed in <var>attrs</var>.
   1493          *
   1494          * <p>Be sure to call {@link TypedArray#recycle() TypedArray.recycle()} when you are done
   1495          * with the array.
   1496          *
   1497          * @param resid The desired style resource.
   1498          * @param attrs The desired attributes in the style.
   1499          *
   1500          * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
   1501          *
   1502          * @return Returns a TypedArray holding an array of the attribute values.
   1503          * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
   1504          * when done with it.
   1505          *
   1506          * @see Resources#obtainAttributes
   1507          * @see #obtainStyledAttributes(int[])
   1508          * @see #obtainStyledAttributes(AttributeSet, int[], int, int)
   1509          */
   1510         public TypedArray obtainStyledAttributes(@StyleRes int resid, @StyleableRes int[] attrs)
   1511                 throws NotFoundException {
   1512             final int len = attrs.length;
   1513             final TypedArray array = TypedArray.obtain(Resources.this, len);
   1514             array.mTheme = this;
   1515             if (false) {
   1516                 int[] data = array.mData;
   1517 
   1518                 System.out.println("**********************************************************");
   1519                 System.out.println("**********************************************************");
   1520                 System.out.println("**********************************************************");
   1521                 System.out.println("Attributes:");
   1522                 String s = "  Attrs:";
   1523                 int i;
   1524                 for (i=0; i<attrs.length; i++) {
   1525                     s = s + " 0x" + Integer.toHexString(attrs[i]);
   1526                 }
   1527                 System.out.println(s);
   1528                 s = "  Found:";
   1529                 TypedValue value = new TypedValue();
   1530                 for (i=0; i<attrs.length; i++) {
   1531                     int d = i*AssetManager.STYLE_NUM_ENTRIES;
   1532                     value.type = data[d+AssetManager.STYLE_TYPE];
   1533                     value.data = data[d+AssetManager.STYLE_DATA];
   1534                     value.assetCookie = data[d+AssetManager.STYLE_ASSET_COOKIE];
   1535                     value.resourceId = data[d+AssetManager.STYLE_RESOURCE_ID];
   1536                     s = s + " 0x" + Integer.toHexString(attrs[i])
   1537                         + "=" + value;
   1538                 }
   1539                 System.out.println(s);
   1540             }
   1541             AssetManager.applyStyle(mTheme, 0, resid, 0, attrs, array.mData, array.mIndices);
   1542             return array;
   1543         }
   1544 
   1545         /**
   1546          * Return a TypedArray holding the attribute values in
   1547          * <var>set</var>
   1548          * that are listed in <var>attrs</var>.  In addition, if the given
   1549          * AttributeSet specifies a style class (through the "style" attribute),
   1550          * that style will be applied on top of the base attributes it defines.
   1551          *
   1552          * <p>Be sure to call {@link TypedArray#recycle() TypedArray.recycle()} when you are done
   1553          * with the array.
   1554          *
   1555          * <p>When determining the final value of a particular attribute, there
   1556          * are four inputs that come into play:</p>
   1557          *
   1558          * <ol>
   1559          *     <li> Any attribute values in the given AttributeSet.
   1560          *     <li> The style resource specified in the AttributeSet (named
   1561          *     "style").
   1562          *     <li> The default style specified by <var>defStyleAttr</var> and
   1563          *     <var>defStyleRes</var>
   1564          *     <li> The base values in this theme.
   1565          * </ol>
   1566          *
   1567          * <p>Each of these inputs is considered in-order, with the first listed
   1568          * taking precedence over the following ones.  In other words, if in the
   1569          * AttributeSet you have supplied <code>&lt;Button
   1570          * textColor="#ff000000"&gt;</code>, then the button's text will
   1571          * <em>always</em> be black, regardless of what is specified in any of
   1572          * the styles.
   1573          *
   1574          * @param set The base set of attribute values.  May be null.
   1575          * @param attrs The desired attributes to be retrieved.
   1576          * @param defStyleAttr An attribute in the current theme that contains a
   1577          *                     reference to a style resource that supplies
   1578          *                     defaults values for the TypedArray.  Can be
   1579          *                     0 to not look for defaults.
   1580          * @param defStyleRes A resource identifier of a style resource that
   1581          *                    supplies default values for the TypedArray,
   1582          *                    used only if defStyleAttr is 0 or can not be found
   1583          *                    in the theme.  Can be 0 to not look for defaults.
   1584          *
   1585          * @return Returns a TypedArray holding an array of the attribute values.
   1586          * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
   1587          * when done with it.
   1588          *
   1589          * @see Resources#obtainAttributes
   1590          * @see #obtainStyledAttributes(int[])
   1591          * @see #obtainStyledAttributes(int, int[])
   1592          */
   1593         public TypedArray obtainStyledAttributes(AttributeSet set,
   1594                 @StyleableRes int[] attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
   1595             final int len = attrs.length;
   1596             final TypedArray array = TypedArray.obtain(Resources.this, len);
   1597 
   1598             // XXX note that for now we only work with compiled XML files.
   1599             // To support generic XML files we will need to manually parse
   1600             // out the attributes from the XML file (applying type information
   1601             // contained in the resources and such).
   1602             final XmlBlock.Parser parser = (XmlBlock.Parser)set;
   1603             AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes,
   1604                     parser != null ? parser.mParseState : 0, attrs, array.mData, array.mIndices);
   1605 
   1606             array.mTheme = this;
   1607             array.mXml = parser;
   1608 
   1609             if (false) {
   1610                 int[] data = array.mData;
   1611 
   1612                 System.out.println("Attributes:");
   1613                 String s = "  Attrs:";
   1614                 int i;
   1615                 for (i=0; i<set.getAttributeCount(); i++) {
   1616                     s = s + " " + set.getAttributeName(i);
   1617                     int id = set.getAttributeNameResource(i);
   1618                     if (id != 0) {
   1619                         s = s + "(0x" + Integer.toHexString(id) + ")";
   1620                     }
   1621                     s = s + "=" + set.getAttributeValue(i);
   1622                 }
   1623                 System.out.println(s);
   1624                 s = "  Found:";
   1625                 TypedValue value = new TypedValue();
   1626                 for (i=0; i<attrs.length; i++) {
   1627                     int d = i*AssetManager.STYLE_NUM_ENTRIES;
   1628                     value.type = data[d+AssetManager.STYLE_TYPE];
   1629                     value.data = data[d+AssetManager.STYLE_DATA];
   1630                     value.assetCookie = data[d+AssetManager.STYLE_ASSET_COOKIE];
   1631                     value.resourceId = data[d+AssetManager.STYLE_RESOURCE_ID];
   1632                     s = s + " 0x" + Integer.toHexString(attrs[i])
   1633                         + "=" + value;
   1634                 }
   1635                 System.out.println(s);
   1636             }
   1637 
   1638             return array;
   1639         }
   1640 
   1641         /**
   1642          * Retrieve the values for a set of attributes in the Theme. The
   1643          * contents of the typed array are ultimately filled in by
   1644          * {@link Resources#getValue}.
   1645          *
   1646          * @param values The base set of attribute values, must be equal in
   1647          *               length to {@code attrs}. All values must be of type
   1648          *               {@link TypedValue#TYPE_ATTRIBUTE}.
   1649          * @param attrs The desired attributes to be retrieved.
   1650          * @return Returns a TypedArray holding an array of the attribute
   1651          *         values. Be sure to call {@link TypedArray#recycle()}
   1652          *         when done with it.
   1653          * @hide
   1654          */
   1655         @NonNull
   1656         public TypedArray resolveAttributes(@NonNull int[] values, @NonNull int[] attrs) {
   1657             final int len = attrs.length;
   1658             if (values == null || len != values.length) {
   1659                 throw new IllegalArgumentException(
   1660                         "Base attribute values must the same length as attrs");
   1661             }
   1662 
   1663             final TypedArray array = TypedArray.obtain(Resources.this, len);
   1664             AssetManager.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices);
   1665             array.mTheme = this;
   1666             array.mXml = null;
   1667 
   1668             return array;
   1669         }
   1670 
   1671         /**
   1672          * Retrieve the value of an attribute in the Theme.  The contents of
   1673          * <var>outValue</var> are ultimately filled in by
   1674          * {@link Resources#getValue}.
   1675          *
   1676          * @param resid The resource identifier of the desired theme
   1677          *              attribute.
   1678          * @param outValue Filled in with the ultimate resource value supplied
   1679          *                 by the attribute.
   1680          * @param resolveRefs If true, resource references will be walked; if
   1681          *                    false, <var>outValue</var> may be a
   1682          *                    TYPE_REFERENCE.  In either case, it will never
   1683          *                    be a TYPE_ATTRIBUTE.
   1684          *
   1685          * @return boolean Returns true if the attribute was found and
   1686          *         <var>outValue</var> is valid, else false.
   1687          */
   1688         public boolean resolveAttribute(int resid, TypedValue outValue, boolean resolveRefs) {
   1689             boolean got = mAssets.getThemeValue(mTheme, resid, outValue, resolveRefs);
   1690             if (false) {
   1691                 System.out.println(
   1692                     "resolveAttribute #" + Integer.toHexString(resid)
   1693                     + " got=" + got + ", type=0x" + Integer.toHexString(outValue.type)
   1694                     + ", data=0x" + Integer.toHexString(outValue.data));
   1695             }
   1696             return got;
   1697         }
   1698 
   1699         /**
   1700          * Gets all of the attribute ids associated with this {@link Theme}. For debugging only.
   1701          *
   1702          * @return The int array containing attribute ids associated with this {@link Theme}.
   1703          * @hide
   1704          */
   1705         public int[] getAllAttributes() {
   1706             return mAssets.getStyleAttributes(getAppliedStyleResId());
   1707         }
   1708 
   1709         /**
   1710          * Returns the resources to which this theme belongs.
   1711          *
   1712          * @return Resources to which this theme belongs.
   1713          */
   1714         public Resources getResources() {
   1715             return Resources.this;
   1716         }
   1717 
   1718         /**
   1719          * Return a drawable object associated with a particular resource ID
   1720          * and styled for the Theme.
   1721          *
   1722          * @param id The desired resource identifier, as generated by the aapt
   1723          *           tool. This integer encodes the package, type, and resource
   1724          *           entry. The value 0 is an invalid identifier.
   1725          * @return Drawable An object that can be used to draw this resource.
   1726          * @throws NotFoundException Throws NotFoundException if the given ID
   1727          *         does not exist.
   1728          */
   1729         public Drawable getDrawable(@DrawableRes int id) throws NotFoundException {
   1730             return Resources.this.getDrawable(id, this);
   1731         }
   1732 
   1733         /**
   1734          * Returns a bit mask of configuration changes that will impact this
   1735          * theme (and thus require completely reloading it).
   1736          *
   1737          * @return a bit mask of configuration changes, as defined by
   1738          *         {@link ActivityInfo}
   1739          * @see ActivityInfo
   1740          */
   1741         public int getChangingConfigurations() {
   1742             final int nativeChangingConfig = AssetManager.getThemeChangingConfigurations(mTheme);
   1743             return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig);
   1744         }
   1745 
   1746         /**
   1747          * Print contents of this theme out to the log.  For debugging only.
   1748          *
   1749          * @param priority The log priority to use.
   1750          * @param tag The log tag to use.
   1751          * @param prefix Text to prefix each line printed.
   1752          */
   1753         public void dump(int priority, String tag, String prefix) {
   1754             AssetManager.dumpTheme(mTheme, priority, tag, prefix);
   1755         }
   1756 
   1757         @Override
   1758         protected void finalize() throws Throwable {
   1759             super.finalize();
   1760             mAssets.releaseTheme(mTheme);
   1761         }
   1762 
   1763         /*package*/ Theme() {
   1764             mAssets = Resources.this.mAssets;
   1765             mTheme = mAssets.createTheme();
   1766         }
   1767 
   1768         /** Unique key for the series of styles applied to this theme. */
   1769         private final ThemeKey mKey = new ThemeKey();
   1770 
   1771         @SuppressWarnings("hiding")
   1772         private final AssetManager mAssets;
   1773         private final long mTheme;
   1774 
   1775         /** Resource identifier for the theme. */
   1776         private int mThemeResId = 0;
   1777 
   1778         // Needed by layoutlib.
   1779         /*package*/ long getNativeTheme() {
   1780             return mTheme;
   1781         }
   1782 
   1783         /*package*/ int getAppliedStyleResId() {
   1784             return mThemeResId;
   1785         }
   1786 
   1787         /*package*/ ThemeKey getKey() {
   1788             return mKey;
   1789         }
   1790 
   1791         private String getResourceNameFromHexString(String hexString) {
   1792             return getResourceName(Integer.parseInt(hexString, 16));
   1793         }
   1794 
   1795         /**
   1796          * Parses {@link #mKey} and returns a String array that holds pairs of
   1797          * adjacent Theme data: resource name followed by whether or not it was
   1798          * forced, as specified by {@link #applyStyle(int, boolean)}.
   1799          *
   1800          * @hide
   1801          */
   1802         @ViewDebug.ExportedProperty(category = "theme", hasAdjacentMapping = true)
   1803         public String[] getTheme() {
   1804             final int N = mKey.mCount;
   1805             final String[] themes = new String[N * 2];
   1806             for (int i = 0, j = N - 1; i < themes.length; i += 2, --j) {
   1807                 final int resId = mKey.mResId[j];
   1808                 final boolean forced = mKey.mForce[j];
   1809                 try {
   1810                     themes[i] = getResourceName(resId);
   1811                 } catch (NotFoundException e) {
   1812                     themes[i] = Integer.toHexString(i);
   1813                 }
   1814                 themes[i + 1] = forced ? "forced" : "not forced";
   1815             }
   1816             return themes;
   1817         }
   1818 
   1819         /** @hide */
   1820         public void encode(@NonNull ViewHierarchyEncoder encoder) {
   1821             encoder.beginObject(this);
   1822             final String[] properties = getTheme();
   1823             for (int i = 0; i < properties.length; i += 2) {
   1824                 encoder.addProperty(properties[i], properties[i+1]);
   1825             }
   1826             encoder.endObject();
   1827         }
   1828 
   1829         /**
   1830          * Rebases the theme against the parent Resource object's current
   1831          * configuration by re-applying the styles passed to
   1832          * {@link #applyStyle(int, boolean)}.
   1833          *
   1834          * @hide
   1835          */
   1836         public void rebase() {
   1837             AssetManager.clearTheme(mTheme);
   1838 
   1839             // Reapply the same styles in the same order.
   1840             for (int i = 0; i < mKey.mCount; i++) {
   1841                 final int resId = mKey.mResId[i];
   1842                 final boolean force = mKey.mForce[i];
   1843                 AssetManager.applyThemeStyle(mTheme, resId, force);
   1844             }
   1845         }
   1846     }
   1847 
   1848     static class ThemeKey implements Cloneable {
   1849         int[] mResId;
   1850         boolean[] mForce;
   1851         int mCount;
   1852 
   1853         private int mHashCode = 0;
   1854 
   1855         public void append(int resId, boolean force) {
   1856             if (mResId == null) {
   1857                 mResId = new int[4];
   1858             }
   1859 
   1860             if (mForce == null) {
   1861                 mForce = new boolean[4];
   1862             }
   1863 
   1864             mResId = GrowingArrayUtils.append(mResId, mCount, resId);
   1865             mForce = GrowingArrayUtils.append(mForce, mCount, force);
   1866             mCount++;
   1867 
   1868             mHashCode = 31 * (31 * mHashCode + resId) + (force ? 1 : 0);
   1869         }
   1870 
   1871         /**
   1872          * Sets up this key as a deep copy of another key.
   1873          *
   1874          * @param other the key to deep copy into this key
   1875          */
   1876         public void setTo(ThemeKey other) {
   1877             mResId = other.mResId == null ? null : other.mResId.clone();
   1878             mForce = other.mForce == null ? null : other.mForce.clone();
   1879             mCount = other.mCount;
   1880         }
   1881 
   1882         @Override
   1883         public int hashCode() {
   1884             return mHashCode;
   1885         }
   1886 
   1887         @Override
   1888         public boolean equals(Object o) {
   1889             if (this == o) {
   1890                 return true;
   1891             }
   1892 
   1893             if (o == null || getClass() != o.getClass() || hashCode() != o.hashCode()) {
   1894                 return false;
   1895             }
   1896 
   1897             final ThemeKey t = (ThemeKey) o;
   1898             if (mCount != t.mCount) {
   1899                 return false;
   1900             }
   1901 
   1902             final int N = mCount;
   1903             for (int i = 0; i < N; i++) {
   1904                 if (mResId[i] != t.mResId[i] || mForce[i] != t.mForce[i]) {
   1905                     return false;
   1906                 }
   1907             }
   1908 
   1909             return true;
   1910         }
   1911 
   1912         /**
   1913          * @return a shallow copy of this key
   1914          */
   1915         @Override
   1916         public ThemeKey clone() {
   1917             final ThemeKey other = new ThemeKey();
   1918             other.mResId = mResId;
   1919             other.mForce = mForce;
   1920             other.mCount = mCount;
   1921             other.mHashCode = mHashCode;
   1922             return other;
   1923         }
   1924     }
   1925 
   1926     /**
   1927      * Generate a new Theme object for this set of Resources.  It initially
   1928      * starts out empty.
   1929      *
   1930      * @return Theme The newly created Theme container.
   1931      */
   1932     public final Theme newTheme() {
   1933         return new Theme();
   1934     }
   1935 
   1936     /**
   1937      * Retrieve a set of basic attribute values from an AttributeSet, not
   1938      * performing styling of them using a theme and/or style resources.
   1939      *
   1940      * @param set The current attribute values to retrieve.
   1941      * @param attrs The specific attributes to be retrieved.
   1942      * @return Returns a TypedArray holding an array of the attribute values.
   1943      * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
   1944      * when done with it.
   1945      *
   1946      * @see Theme#obtainStyledAttributes(AttributeSet, int[], int, int)
   1947      */
   1948     public TypedArray obtainAttributes(AttributeSet set, int[] attrs) {
   1949         int len = attrs.length;
   1950         TypedArray array = TypedArray.obtain(this, len);
   1951 
   1952         // XXX note that for now we only work with compiled XML files.
   1953         // To support generic XML files we will need to manually parse
   1954         // out the attributes from the XML file (applying type information
   1955         // contained in the resources and such).
   1956         XmlBlock.Parser parser = (XmlBlock.Parser)set;
   1957         mAssets.retrieveAttributes(parser.mParseState, attrs,
   1958                 array.mData, array.mIndices);
   1959 
   1960         array.mXml = parser;
   1961 
   1962         return array;
   1963     }
   1964 
   1965     /**
   1966      * Store the newly updated configuration.
   1967      */
   1968     public void updateConfiguration(Configuration config,
   1969             DisplayMetrics metrics) {
   1970         updateConfiguration(config, metrics, null);
   1971     }
   1972 
   1973     /**
   1974      * @hide
   1975      */
   1976     public void updateConfiguration(Configuration config,
   1977             DisplayMetrics metrics, CompatibilityInfo compat) {
   1978         synchronized (mAccessLock) {
   1979             if (false) {
   1980                 Slog.i(TAG, "**** Updating config of " + this + ": old config is "
   1981                         + mConfiguration + " old compat is " + mCompatibilityInfo);
   1982                 Slog.i(TAG, "**** Updating config of " + this + ": new config is "
   1983                         + config + " new compat is " + compat);
   1984             }
   1985             if (compat != null) {
   1986                 mCompatibilityInfo = compat;
   1987             }
   1988             if (metrics != null) {
   1989                 mMetrics.setTo(metrics);
   1990             }
   1991             // NOTE: We should re-arrange this code to create a Display
   1992             // with the CompatibilityInfo that is used everywhere we deal
   1993             // with the display in relation to this app, rather than
   1994             // doing the conversion here.  This impl should be okay because
   1995             // we make sure to return a compatible display in the places
   1996             // where there are public APIs to retrieve the display...  but
   1997             // it would be cleaner and more maintainble to just be
   1998             // consistently dealing with a compatible display everywhere in
   1999             // the framework.
   2000             mCompatibilityInfo.applyToDisplayMetrics(mMetrics);
   2001 
   2002             final int configChanges = calcConfigChanges(config);
   2003             if (mConfiguration.locale == null) {
   2004                 mConfiguration.locale = Locale.getDefault();
   2005                 mConfiguration.setLayoutDirection(mConfiguration.locale);
   2006             }
   2007             if (mConfiguration.densityDpi != Configuration.DENSITY_DPI_UNDEFINED) {
   2008                 mMetrics.densityDpi = mConfiguration.densityDpi;
   2009                 mMetrics.density = mConfiguration.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
   2010             }
   2011             mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale;
   2012 
   2013             String locale = null;
   2014             if (mConfiguration.locale != null) {
   2015                 locale = adjustLanguageTag(mConfiguration.locale.toLanguageTag());
   2016             }
   2017 
   2018             final int width, height;
   2019             if (mMetrics.widthPixels >= mMetrics.heightPixels) {
   2020                 width = mMetrics.widthPixels;
   2021                 height = mMetrics.heightPixels;
   2022             } else {
   2023                 //noinspection SuspiciousNameCombination
   2024                 width = mMetrics.heightPixels;
   2025                 //noinspection SuspiciousNameCombination
   2026                 height = mMetrics.widthPixels;
   2027             }
   2028 
   2029             final int keyboardHidden;
   2030             if (mConfiguration.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO
   2031                     && mConfiguration.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) {
   2032                 keyboardHidden = Configuration.KEYBOARDHIDDEN_SOFT;
   2033             } else {
   2034                 keyboardHidden = mConfiguration.keyboardHidden;
   2035             }
   2036 
   2037             mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
   2038                     locale, mConfiguration.orientation,
   2039                     mConfiguration.touchscreen,
   2040                     mConfiguration.densityDpi, mConfiguration.keyboard,
   2041                     keyboardHidden, mConfiguration.navigation, width, height,
   2042                     mConfiguration.smallestScreenWidthDp,
   2043                     mConfiguration.screenWidthDp, mConfiguration.screenHeightDp,
   2044                     mConfiguration.screenLayout, mConfiguration.uiMode,
   2045                     Build.VERSION.RESOURCES_SDK_INT);
   2046 
   2047             if (DEBUG_CONFIG) {
   2048                 Slog.i(TAG, "**** Updating config of " + this + ": final config is " + mConfiguration
   2049                         + " final compat is " + mCompatibilityInfo);
   2050             }
   2051 
   2052             mDrawableCache.onConfigurationChange(configChanges);
   2053             mColorDrawableCache.onConfigurationChange(configChanges);
   2054             mColorStateListCache.onConfigurationChange(configChanges);
   2055             mAnimatorCache.onConfigurationChange(configChanges);
   2056             mStateListAnimatorCache.onConfigurationChange(configChanges);
   2057 
   2058             flushLayoutCache();
   2059         }
   2060         synchronized (sSync) {
   2061             if (mPluralRule != null) {
   2062                 mPluralRule = NativePluralRules.forLocale(config.locale);
   2063             }
   2064         }
   2065     }
   2066 
   2067     /**
   2068      * Called by ConfigurationBoundResourceCacheTest via reflection.
   2069      */
   2070     private int calcConfigChanges(Configuration config) {
   2071         int configChanges = 0xfffffff;
   2072         if (config != null) {
   2073             mTmpConfig.setTo(config);
   2074             int density = config.densityDpi;
   2075             if (density == Configuration.DENSITY_DPI_UNDEFINED) {
   2076                 density = mMetrics.noncompatDensityDpi;
   2077             }
   2078 
   2079             mCompatibilityInfo.applyToConfiguration(density, mTmpConfig);
   2080 
   2081             if (mTmpConfig.locale == null) {
   2082                 mTmpConfig.locale = Locale.getDefault();
   2083                 mTmpConfig.setLayoutDirection(mTmpConfig.locale);
   2084             }
   2085             configChanges = mConfiguration.updateFrom(mTmpConfig);
   2086             configChanges = ActivityInfo.activityInfoConfigToNative(configChanges);
   2087         }
   2088         return configChanges;
   2089     }
   2090 
   2091     /**
   2092      * {@code Locale.toLanguageTag} will transform the obsolete (and deprecated)
   2093      * language codes "in", "ji" and "iw" to "id", "yi" and "he" respectively.
   2094      *
   2095      * All released versions of android prior to "L" used the deprecated language
   2096      * tags, so we will need to support them for backwards compatibility.
   2097      *
   2098      * Note that this conversion needs to take place *after* the call to
   2099      * {@code toLanguageTag} because that will convert all the deprecated codes to
   2100      * the new ones, even if they're set manually.
   2101      */
   2102     private static String adjustLanguageTag(String languageTag) {
   2103         final int separator = languageTag.indexOf('-');
   2104         final String language;
   2105         final String remainder;
   2106 
   2107         if (separator == -1) {
   2108             language = languageTag;
   2109             remainder = "";
   2110         } else {
   2111             language = languageTag.substring(0, separator);
   2112             remainder = languageTag.substring(separator);
   2113         }
   2114 
   2115         return Locale.adjustLanguageCode(language) + remainder;
   2116     }
   2117 
   2118     /**
   2119      * Update the system resources configuration if they have previously
   2120      * been initialized.
   2121      *
   2122      * @hide
   2123      */
   2124     public static void updateSystemConfiguration(Configuration config, DisplayMetrics metrics,
   2125             CompatibilityInfo compat) {
   2126         if (mSystem != null) {
   2127             mSystem.updateConfiguration(config, metrics, compat);
   2128             //Log.i(TAG, "Updated system resources " + mSystem
   2129             //        + ": " + mSystem.getConfiguration());
   2130         }
   2131     }
   2132 
   2133     /**
   2134      * Return the current display metrics that are in effect for this resource
   2135      * object.  The returned object should be treated as read-only.
   2136      *
   2137      * @return The resource's current display metrics.
   2138      */
   2139     public DisplayMetrics getDisplayMetrics() {
   2140         if (DEBUG_CONFIG) Slog.v(TAG, "Returning DisplayMetrics: " + mMetrics.widthPixels
   2141                 + "x" + mMetrics.heightPixels + " " + mMetrics.density);
   2142         return mMetrics;
   2143     }
   2144 
   2145     /**
   2146      * Return the current configuration that is in effect for this resource
   2147      * object.  The returned object should be treated as read-only.
   2148      *
   2149      * @return The resource's current configuration.
   2150      */
   2151     public Configuration getConfiguration() {
   2152         return mConfiguration;
   2153     }
   2154 
   2155     /**
   2156      * Return the compatibility mode information for the application.
   2157      * The returned object should be treated as read-only.
   2158      *
   2159      * @return compatibility info.
   2160      * @hide
   2161      */
   2162     public CompatibilityInfo getCompatibilityInfo() {
   2163         return mCompatibilityInfo;
   2164     }
   2165 
   2166     /**
   2167      * This is just for testing.
   2168      * @hide
   2169      */
   2170     public void setCompatibilityInfo(CompatibilityInfo ci) {
   2171         if (ci != null) {
   2172             mCompatibilityInfo = ci;
   2173             updateConfiguration(mConfiguration, mMetrics);
   2174         }
   2175     }
   2176 
   2177     /**
   2178      * Return a resource identifier for the given resource name.  A fully
   2179      * qualified resource name is of the form "package:type/entry".  The first
   2180      * two components (package and type) are optional if defType and
   2181      * defPackage, respectively, are specified here.
   2182      *
   2183      * <p>Note: use of this function is discouraged.  It is much more
   2184      * efficient to retrieve resources by identifier than by name.
   2185      *
   2186      * @param name The name of the desired resource.
   2187      * @param defType Optional default resource type to find, if "type/" is
   2188      *                not included in the name.  Can be null to require an
   2189      *                explicit type.
   2190      * @param defPackage Optional default package to find, if "package:" is
   2191      *                   not included in the name.  Can be null to require an
   2192      *                   explicit package.
   2193      *
   2194      * @return int The associated resource identifier.  Returns 0 if no such
   2195      *         resource was found.  (0 is not a valid resource ID.)
   2196      */
   2197     public int getIdentifier(String name, String defType, String defPackage) {
   2198         if (name == null) {
   2199             throw new NullPointerException("name is null");
   2200         }
   2201         try {
   2202             return Integer.parseInt(name);
   2203         } catch (Exception e) {
   2204             // Ignore
   2205         }
   2206         return mAssets.getResourceIdentifier(name, defType, defPackage);
   2207     }
   2208 
   2209     /**
   2210      * Return true if given resource identifier includes a package.
   2211      *
   2212      * @hide
   2213      */
   2214     public static boolean resourceHasPackage(@AnyRes int resid) {
   2215         return (resid >>> 24) != 0;
   2216     }
   2217 
   2218     /**
   2219      * Return the full name for a given resource identifier.  This name is
   2220      * a single string of the form "package:type/entry".
   2221      *
   2222      * @param resid The resource identifier whose name is to be retrieved.
   2223      *
   2224      * @return A string holding the name of the resource.
   2225      *
   2226      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
   2227      *
   2228      * @see #getResourcePackageName
   2229      * @see #getResourceTypeName
   2230      * @see #getResourceEntryName
   2231      */
   2232     public String getResourceName(@AnyRes int resid) throws NotFoundException {
   2233         String str = mAssets.getResourceName(resid);
   2234         if (str != null) return str;
   2235         throw new NotFoundException("Unable to find resource ID #0x"
   2236                 + Integer.toHexString(resid));
   2237     }
   2238 
   2239     /**
   2240      * Return the package name for a given resource identifier.
   2241      *
   2242      * @param resid The resource identifier whose package name is to be
   2243      * retrieved.
   2244      *
   2245      * @return A string holding the package name of the resource.
   2246      *
   2247      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
   2248      *
   2249      * @see #getResourceName
   2250      */
   2251     public String getResourcePackageName(@AnyRes int resid) throws NotFoundException {
   2252         String str = mAssets.getResourcePackageName(resid);
   2253         if (str != null) return str;
   2254         throw new NotFoundException("Unable to find resource ID #0x"
   2255                 + Integer.toHexString(resid));
   2256     }
   2257 
   2258     /**
   2259      * Return the type name for a given resource identifier.
   2260      *
   2261      * @param resid The resource identifier whose type name is to be
   2262      * retrieved.
   2263      *
   2264      * @return A string holding the type name of the resource.
   2265      *
   2266      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
   2267      *
   2268      * @see #getResourceName
   2269      */
   2270     public String getResourceTypeName(@AnyRes int resid) throws NotFoundException {
   2271         String str = mAssets.getResourceTypeName(resid);
   2272         if (str != null) return str;
   2273         throw new NotFoundException("Unable to find resource ID #0x"
   2274                 + Integer.toHexString(resid));
   2275     }
   2276 
   2277     /**
   2278      * Return the entry name for a given resource identifier.
   2279      *
   2280      * @param resid The resource identifier whose entry name is to be
   2281      * retrieved.
   2282      *
   2283      * @return A string holding the entry name of the resource.
   2284      *
   2285      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
   2286      *
   2287      * @see #getResourceName
   2288      */
   2289     public String getResourceEntryName(@AnyRes int resid) throws NotFoundException {
   2290         String str = mAssets.getResourceEntryName(resid);
   2291         if (str != null) return str;
   2292         throw new NotFoundException("Unable to find resource ID #0x"
   2293                 + Integer.toHexString(resid));
   2294     }
   2295 
   2296     /**
   2297      * Parse a series of {@link android.R.styleable#Extra &lt;extra&gt;} tags from
   2298      * an XML file.  You call this when you are at the parent tag of the
   2299      * extra tags, and it will return once all of the child tags have been parsed.
   2300      * This will call {@link #parseBundleExtra} for each extra tag encountered.
   2301      *
   2302      * @param parser The parser from which to retrieve the extras.
   2303      * @param outBundle A Bundle in which to place all parsed extras.
   2304      * @throws XmlPullParserException
   2305      * @throws IOException
   2306      */
   2307     public void parseBundleExtras(XmlResourceParser parser, Bundle outBundle)
   2308             throws XmlPullParserException, IOException {
   2309         int outerDepth = parser.getDepth();
   2310         int type;
   2311         while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
   2312                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
   2313             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
   2314                 continue;
   2315             }
   2316 
   2317             String nodeName = parser.getName();
   2318             if (nodeName.equals("extra")) {
   2319                 parseBundleExtra("extra", parser, outBundle);
   2320                 XmlUtils.skipCurrentTag(parser);
   2321 
   2322             } else {
   2323                 XmlUtils.skipCurrentTag(parser);
   2324             }
   2325         }
   2326     }
   2327 
   2328     /**
   2329      * Parse a name/value pair out of an XML tag holding that data.  The
   2330      * AttributeSet must be holding the data defined by
   2331      * {@link android.R.styleable#Extra}.  The following value types are supported:
   2332      * <ul>
   2333      * <li> {@link TypedValue#TYPE_STRING}:
   2334      * {@link Bundle#putCharSequence Bundle.putCharSequence()}
   2335      * <li> {@link TypedValue#TYPE_INT_BOOLEAN}:
   2336      * {@link Bundle#putCharSequence Bundle.putBoolean()}
   2337      * <li> {@link TypedValue#TYPE_FIRST_INT}-{@link TypedValue#TYPE_LAST_INT}:
   2338      * {@link Bundle#putCharSequence Bundle.putBoolean()}
   2339      * <li> {@link TypedValue#TYPE_FLOAT}:
   2340      * {@link Bundle#putCharSequence Bundle.putFloat()}
   2341      * </ul>
   2342      *
   2343      * @param tagName The name of the tag these attributes come from; this is
   2344      * only used for reporting error messages.
   2345      * @param attrs The attributes from which to retrieve the name/value pair.
   2346      * @param outBundle The Bundle in which to place the parsed value.
   2347      * @throws XmlPullParserException If the attributes are not valid.
   2348      */
   2349     public void parseBundleExtra(String tagName, AttributeSet attrs,
   2350             Bundle outBundle) throws XmlPullParserException {
   2351         TypedArray sa = obtainAttributes(attrs,
   2352                 com.android.internal.R.styleable.Extra);
   2353 
   2354         String name = sa.getString(
   2355                 com.android.internal.R.styleable.Extra_name);
   2356         if (name == null) {
   2357             sa.recycle();
   2358             throw new XmlPullParserException("<" + tagName
   2359                     + "> requires an android:name attribute at "
   2360                     + attrs.getPositionDescription());
   2361         }
   2362 
   2363         TypedValue v = sa.peekValue(
   2364                 com.android.internal.R.styleable.Extra_value);
   2365         if (v != null) {
   2366             if (v.type == TypedValue.TYPE_STRING) {
   2367                 CharSequence cs = v.coerceToString();
   2368                 outBundle.putCharSequence(name, cs);
   2369             } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) {
   2370                 outBundle.putBoolean(name, v.data != 0);
   2371             } else if (v.type >= TypedValue.TYPE_FIRST_INT
   2372                     && v.type <= TypedValue.TYPE_LAST_INT) {
   2373                 outBundle.putInt(name, v.data);
   2374             } else if (v.type == TypedValue.TYPE_FLOAT) {
   2375                 outBundle.putFloat(name, v.getFloat());
   2376             } else {
   2377                 sa.recycle();
   2378                 throw new XmlPullParserException("<" + tagName
   2379                         + "> only supports string, integer, float, color, and boolean at "
   2380                         + attrs.getPositionDescription());
   2381             }
   2382         } else {
   2383             sa.recycle();
   2384             throw new XmlPullParserException("<" + tagName
   2385                     + "> requires an android:value or android:resource attribute at "
   2386                     + attrs.getPositionDescription());
   2387         }
   2388 
   2389         sa.recycle();
   2390     }
   2391 
   2392     /**
   2393      * Retrieve underlying AssetManager storage for these resources.
   2394      */
   2395     public final AssetManager getAssets() {
   2396         return mAssets;
   2397     }
   2398 
   2399     /**
   2400      * Call this to remove all cached loaded layout resources from the
   2401      * Resources object.  Only intended for use with performance testing
   2402      * tools.
   2403      */
   2404     public final void flushLayoutCache() {
   2405         synchronized (mCachedXmlBlockIds) {
   2406             // First see if this block is in our cache.
   2407             final int num = mCachedXmlBlockIds.length;
   2408             for (int i=0; i<num; i++) {
   2409                 mCachedXmlBlockIds[i] = -0;
   2410                 XmlBlock oldBlock = mCachedXmlBlocks[i];
   2411                 if (oldBlock != null) {
   2412                     oldBlock.close();
   2413                 }
   2414                 mCachedXmlBlocks[i] = null;
   2415             }
   2416         }
   2417     }
   2418 
   2419     /**
   2420      * Start preloading of resource data using this Resources object.  Only
   2421      * for use by the zygote process for loading common system resources.
   2422      * {@hide}
   2423      */
   2424     public final void startPreloading() {
   2425         synchronized (sSync) {
   2426             if (sPreloaded) {
   2427                 throw new IllegalStateException("Resources already preloaded");
   2428             }
   2429             sPreloaded = true;
   2430             mPreloading = true;
   2431             sPreloadedDensity = DisplayMetrics.DENSITY_DEVICE;
   2432             mConfiguration.densityDpi = sPreloadedDensity;
   2433             updateConfiguration(null, null);
   2434         }
   2435     }
   2436 
   2437     /**
   2438      * Called by zygote when it is done preloading resources, to change back
   2439      * to normal Resources operation.
   2440      */
   2441     public final void finishPreloading() {
   2442         if (mPreloading) {
   2443             mPreloading = false;
   2444             flushLayoutCache();
   2445         }
   2446     }
   2447 
   2448     /**
   2449      * @hide
   2450      */
   2451     public LongSparseArray<ConstantState> getPreloadedDrawables() {
   2452         return sPreloadedDrawables[0];
   2453     }
   2454 
   2455     private boolean verifyPreloadConfig(int changingConfigurations, int allowVarying,
   2456             int resourceId, String name) {
   2457         // We allow preloading of resources even if they vary by font scale (which
   2458         // doesn't impact resource selection) or density (which we handle specially by
   2459         // simply turning off all preloading), as well as any other configs specified
   2460         // by the caller.
   2461         if (((changingConfigurations&~(ActivityInfo.CONFIG_FONT_SCALE |
   2462                 ActivityInfo.CONFIG_DENSITY)) & ~allowVarying) != 0) {
   2463             String resName;
   2464             try {
   2465                 resName = getResourceName(resourceId);
   2466             } catch (NotFoundException e) {
   2467                 resName = "?";
   2468             }
   2469             // This should never happen in production, so we should log a
   2470             // warning even if we're not debugging.
   2471             Log.w(TAG, "Preloaded " + name + " resource #0x"
   2472                     + Integer.toHexString(resourceId)
   2473                     + " (" + resName + ") that varies with configuration!!");
   2474             return false;
   2475         }
   2476         if (TRACE_FOR_PRELOAD) {
   2477             String resName;
   2478             try {
   2479                 resName = getResourceName(resourceId);
   2480             } catch (NotFoundException e) {
   2481                 resName = "?";
   2482             }
   2483             Log.w(TAG, "Preloading " + name + " resource #0x"
   2484                     + Integer.toHexString(resourceId)
   2485                     + " (" + resName + ")");
   2486         }
   2487         return true;
   2488     }
   2489 
   2490     @Nullable
   2491     Drawable loadDrawable(TypedValue value, int id, Theme theme) throws NotFoundException {
   2492         if (TRACE_FOR_PRELOAD) {
   2493             // Log only framework resources
   2494             if ((id >>> 24) == 0x1) {
   2495                 final String name = getResourceName(id);
   2496                 if (name != null) {
   2497                     Log.d("PreloadDrawable", name);
   2498                 }
   2499             }
   2500         }
   2501 
   2502         final boolean isColorDrawable;
   2503         final DrawableCache caches;
   2504         final long key;
   2505         if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
   2506                 && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
   2507             isColorDrawable = true;
   2508             caches = mColorDrawableCache;
   2509             key = value.data;
   2510         } else {
   2511             isColorDrawable = false;
   2512             caches = mDrawableCache;
   2513             key = (((long) value.assetCookie) << 32) | value.data;
   2514         }
   2515 
   2516         // First, check whether we have a cached version of this drawable
   2517         // that was inflated against the specified theme.
   2518         if (!mPreloading) {
   2519             final Drawable cachedDrawable = caches.getInstance(key, theme);
   2520             if (cachedDrawable != null) {
   2521                 return cachedDrawable;
   2522             }
   2523         }
   2524 
   2525         // Next, check preloaded drawables. These may contain unresolved theme
   2526         // attributes.
   2527         final ConstantState cs;
   2528         if (isColorDrawable) {
   2529             cs = sPreloadedColorDrawables.get(key);
   2530         } else {
   2531             cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key);
   2532         }
   2533 
   2534         Drawable dr;
   2535         if (cs != null) {
   2536             dr = cs.newDrawable(this);
   2537         } else if (isColorDrawable) {
   2538             dr = new ColorDrawable(value.data);
   2539         } else {
   2540             dr = loadDrawableForCookie(value, id, null);
   2541         }
   2542 
   2543         // Determine if the drawable has unresolved theme attributes. If it
   2544         // does, we'll need to apply a theme and store it in a theme-specific
   2545         // cache.
   2546         final boolean canApplyTheme = dr != null && dr.canApplyTheme();
   2547         if (canApplyTheme && theme != null) {
   2548             dr = dr.mutate();
   2549             dr.applyTheme(theme);
   2550             dr.clearMutated();
   2551         }
   2552 
   2553         // If we were able to obtain a drawable, store it in the appropriate
   2554         // cache: preload, not themed, null theme, or theme-specific.
   2555         if (dr != null) {
   2556             dr.setChangingConfigurations(value.changingConfigurations);
   2557             cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr);
   2558         }
   2559 
   2560         return dr;
   2561     }
   2562 
   2563     private void cacheDrawable(TypedValue value, boolean isColorDrawable, DrawableCache caches,
   2564             Theme theme, boolean usesTheme, long key, Drawable dr) {
   2565         final ConstantState cs = dr.getConstantState();
   2566         if (cs == null) {
   2567             return;
   2568         }
   2569 
   2570         if (mPreloading) {
   2571             final int changingConfigs = cs.getChangingConfigurations();
   2572             if (isColorDrawable) {
   2573                 if (verifyPreloadConfig(changingConfigs, 0, value.resourceId, "drawable")) {
   2574                     sPreloadedColorDrawables.put(key, cs);
   2575                 }
   2576             } else {
   2577                 if (verifyPreloadConfig(
   2578                         changingConfigs, LAYOUT_DIR_CONFIG, value.resourceId, "drawable")) {
   2579                     if ((changingConfigs & LAYOUT_DIR_CONFIG) == 0) {
   2580                         // If this resource does not vary based on layout direction,
   2581                         // we can put it in all of the preload maps.
   2582                         sPreloadedDrawables[0].put(key, cs);
   2583                         sPreloadedDrawables[1].put(key, cs);
   2584                     } else {
   2585                         // Otherwise, only in the layout dir we loaded it for.
   2586                         sPreloadedDrawables[mConfiguration.getLayoutDirection()].put(key, cs);
   2587                     }
   2588                 }
   2589             }
   2590         } else {
   2591             synchronized (mAccessLock) {
   2592                 caches.put(key, theme, cs, usesTheme);
   2593             }
   2594         }
   2595     }
   2596 
   2597     /**
   2598      * Loads a drawable from XML or resources stream.
   2599      */
   2600     private Drawable loadDrawableForCookie(TypedValue value, int id, Theme theme) {
   2601         if (value.string == null) {
   2602             throw new NotFoundException("Resource \"" + getResourceName(id) + "\" ("
   2603                     + Integer.toHexString(id) + ") is not a Drawable (color or path): " + value);
   2604         }
   2605 
   2606         final String file = value.string.toString();
   2607 
   2608         if (TRACE_FOR_MISS_PRELOAD) {
   2609             // Log only framework resources
   2610             if ((id >>> 24) == 0x1) {
   2611                 final String name = getResourceName(id);
   2612                 if (name != null) {
   2613                     Log.d(TAG, "Loading framework drawable #" + Integer.toHexString(id)
   2614                             + ": " + name + " at " + file);
   2615                 }
   2616             }
   2617         }
   2618 
   2619         if (DEBUG_LOAD) {
   2620             Log.v(TAG, "Loading drawable for cookie " + value.assetCookie + ": " + file);
   2621         }
   2622 
   2623         final Drawable dr;
   2624 
   2625         Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
   2626         try {
   2627             if (file.endsWith(".xml")) {
   2628                 final XmlResourceParser rp = loadXmlResourceParser(
   2629                         file, id, value.assetCookie, "drawable");
   2630                 dr = Drawable.createFromXml(this, rp, theme);
   2631                 rp.close();
   2632             } else {
   2633                 final InputStream is = mAssets.openNonAsset(
   2634                         value.assetCookie, file, AssetManager.ACCESS_STREAMING);
   2635                 dr = Drawable.createFromResourceStream(this, value, is, file, null);
   2636                 is.close();
   2637             }
   2638         } catch (Exception e) {
   2639             Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
   2640             final NotFoundException rnf = new NotFoundException(
   2641                     "File " + file + " from drawable resource ID #0x" + Integer.toHexString(id));
   2642             rnf.initCause(e);
   2643             throw rnf;
   2644         }
   2645         Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
   2646 
   2647         return dr;
   2648     }
   2649 
   2650     @Nullable
   2651     ColorStateList loadColorStateList(TypedValue value, int id, Theme theme)
   2652             throws NotFoundException {
   2653         if (TRACE_FOR_PRELOAD) {
   2654             // Log only framework resources
   2655             if ((id >>> 24) == 0x1) {
   2656                 final String name = getResourceName(id);
   2657                 if (name != null) android.util.Log.d("PreloadColorStateList", name);
   2658             }
   2659         }
   2660 
   2661         final long key = (((long) value.assetCookie) << 32) | value.data;
   2662 
   2663         ColorStateList csl;
   2664 
   2665         // Handle inline color definitions.
   2666         if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
   2667                 && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
   2668             final android.content.res.ConstantState<ColorStateList> factory =
   2669                     sPreloadedColorStateLists.get(key);
   2670             if (factory != null) {
   2671                 return factory.newInstance();
   2672             }
   2673 
   2674             csl = ColorStateList.valueOf(value.data);
   2675 
   2676             if (mPreloading) {
   2677                 if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId,
   2678                         "color")) {
   2679                     sPreloadedColorStateLists.put(key, csl.getConstantState());
   2680                 }
   2681             }
   2682 
   2683             return csl;
   2684         }
   2685 
   2686         final ConfigurationBoundResourceCache<ColorStateList> cache = mColorStateListCache;
   2687         csl = cache.getInstance(key, theme);
   2688         if (csl != null) {
   2689             return csl;
   2690         }
   2691 
   2692         final android.content.res.ConstantState<ColorStateList> factory =
   2693                 sPreloadedColorStateLists.get(key);
   2694         if (factory != null) {
   2695             csl = factory.newInstance(this, theme);
   2696         }
   2697 
   2698         if (csl == null) {
   2699             csl = loadColorStateListForCookie(value, id, theme);
   2700         }
   2701 
   2702         if (csl != null) {
   2703             if (mPreloading) {
   2704                 if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId,
   2705                         "color")) {
   2706                     sPreloadedColorStateLists.put(key, csl.getConstantState());
   2707                 }
   2708             } else {
   2709                 cache.put(key, theme, csl.getConstantState());
   2710             }
   2711         }
   2712 
   2713         return csl;
   2714     }
   2715 
   2716     private ColorStateList loadColorStateListForCookie(TypedValue value, int id, Theme theme) {
   2717         if (value.string == null) {
   2718             throw new UnsupportedOperationException(
   2719                     "Can't convert to color state list: type=0x" + value.type);
   2720         }
   2721 
   2722         final String file = value.string.toString();
   2723 
   2724         if (TRACE_FOR_MISS_PRELOAD) {
   2725             // Log only framework resources
   2726             if ((id >>> 24) == 0x1) {
   2727                 final String name = getResourceName(id);
   2728                 if (name != null) {
   2729                     Log.d(TAG, "Loading framework color state list #" + Integer.toHexString(id)
   2730                             + ": " + name + " at " + file);
   2731                 }
   2732             }
   2733         }
   2734 
   2735         if (DEBUG_LOAD) {
   2736             Log.v(TAG, "Loading color state list for cookie " + value.assetCookie + ": " + file);
   2737         }
   2738 
   2739         final ColorStateList csl;
   2740 
   2741         Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
   2742         if (file.endsWith(".xml")) {
   2743             try {
   2744                 final XmlResourceParser rp = loadXmlResourceParser(
   2745                         file, id, value.assetCookie, "colorstatelist");
   2746                 csl = ColorStateList.createFromXml(this, rp, theme);
   2747                 rp.close();
   2748             } catch (Exception e) {
   2749                 Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
   2750                 final NotFoundException rnf = new NotFoundException(
   2751                         "File " + file + " from color state list resource ID #0x"
   2752                                 + Integer.toHexString(id));
   2753                 rnf.initCause(e);
   2754                 throw rnf;
   2755             }
   2756         } else {
   2757             Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
   2758             throw new NotFoundException(
   2759                     "File " + file + " from drawable resource ID #0x"
   2760                             + Integer.toHexString(id) + ": .xml extension required");
   2761         }
   2762         Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
   2763 
   2764         return csl;
   2765     }
   2766 
   2767     /*package*/ XmlResourceParser loadXmlResourceParser(int id, String type)
   2768             throws NotFoundException {
   2769         synchronized (mAccessLock) {
   2770             TypedValue value = mTmpValue;
   2771             if (value == null) {
   2772                 mTmpValue = value = new TypedValue();
   2773             }
   2774             getValue(id, value, true);
   2775             if (value.type == TypedValue.TYPE_STRING) {
   2776                 return loadXmlResourceParser(value.string.toString(), id,
   2777                         value.assetCookie, type);
   2778             }
   2779             throw new NotFoundException(
   2780                     "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
   2781                     + Integer.toHexString(value.type) + " is not valid");
   2782         }
   2783     }
   2784 
   2785     /*package*/ XmlResourceParser loadXmlResourceParser(String file, int id,
   2786             int assetCookie, String type) throws NotFoundException {
   2787         if (id != 0) {
   2788             try {
   2789                 // These may be compiled...
   2790                 synchronized (mCachedXmlBlockIds) {
   2791                     // First see if this block is in our cache.
   2792                     final int num = mCachedXmlBlockIds.length;
   2793                     for (int i=0; i<num; i++) {
   2794                         if (mCachedXmlBlockIds[i] == id) {
   2795                             //System.out.println("**** REUSING XML BLOCK!  id="
   2796                             //                   + id + ", index=" + i);
   2797                             return mCachedXmlBlocks[i].newParser();
   2798                         }
   2799                     }
   2800 
   2801                     // Not in the cache, create a new block and put it at
   2802                     // the next slot in the cache.
   2803                     XmlBlock block = mAssets.openXmlBlockAsset(
   2804                             assetCookie, file);
   2805                     if (block != null) {
   2806                         int pos = mLastCachedXmlBlockIndex+1;
   2807                         if (pos >= num) pos = 0;
   2808                         mLastCachedXmlBlockIndex = pos;
   2809                         XmlBlock oldBlock = mCachedXmlBlocks[pos];
   2810                         if (oldBlock != null) {
   2811                             oldBlock.close();
   2812                         }
   2813                         mCachedXmlBlockIds[pos] = id;
   2814                         mCachedXmlBlocks[pos] = block;
   2815                         //System.out.println("**** CACHING NEW XML BLOCK!  id="
   2816                         //                   + id + ", index=" + pos);
   2817                         return block.newParser();
   2818                     }
   2819                 }
   2820             } catch (Exception e) {
   2821                 NotFoundException rnf = new NotFoundException(
   2822                         "File " + file + " from xml type " + type + " resource ID #0x"
   2823                         + Integer.toHexString(id));
   2824                 rnf.initCause(e);
   2825                 throw rnf;
   2826             }
   2827         }
   2828 
   2829         throw new NotFoundException(
   2830                 "File " + file + " from xml type " + type + " resource ID #0x"
   2831                 + Integer.toHexString(id));
   2832     }
   2833 
   2834     /**
   2835      * Obtains styled attributes from the theme, if available, or unstyled
   2836      * resources if the theme is null.
   2837      *
   2838      * @hide
   2839      */
   2840     public static TypedArray obtainAttributes(
   2841             Resources res, Theme theme, AttributeSet set, int[] attrs) {
   2842         if (theme == null) {
   2843             return res.obtainAttributes(set, attrs);
   2844         }
   2845         return theme.obtainStyledAttributes(set, attrs, 0, 0);
   2846     }
   2847 
   2848     private Resources() {
   2849         mAssets = AssetManager.getSystem();
   2850         // NOTE: Intentionally leaving this uninitialized (all values set
   2851         // to zero), so that anyone who tries to do something that requires
   2852         // metrics will get a very wrong value.
   2853         mConfiguration.setToDefaults();
   2854         mMetrics.setToDefaults();
   2855         updateConfiguration(null, null);
   2856         mAssets.ensureStringBlocks();
   2857     }
   2858 }
   2859