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      */
selectDefaultTheme(int curTheme, int targetSdkVersion)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 */
selectSystemTheme(int curTheme, int targetSdkVersion, int orig, int holo, int dark, int deviceDefault)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      */
getAnimatorCache()212     public ConfigurationBoundResourceCache<Animator> getAnimatorCache() {
213         return mAnimatorCache;
214     }
215 
216     /**
217      * Used by AnimatorInflater.
218      *
219      * @hide
220      */
getStateListAnimatorCache()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 {
NotFoundException()230         public NotFoundException() {
231         }
232 
NotFoundException(String name)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      */
Resources(AssetManager assets, DisplayMetrics metrics, Configuration config)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      */
Resources(AssetManager assets, DisplayMetrics metrics, Configuration config, CompatibilityInfo compatInfo)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      */
getSystem()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      */
getText(@tringRes int id)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      */
getQuantityText(@luralsRes int id, int quantity)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 
getPluralRule()353     private NativePluralRules getPluralRule() {
354         synchronized (sSync) {
355             if (mPluralRule == null) {
356                 mPluralRule = NativePluralRules.forLocale(mConfiguration.locale);
357             }
358             return mPluralRule;
359         }
360     }
361 
attrForQuantityCode(int quantityCode)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 
stringForQuantityCode(int quantityCode)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
getString(@tringRes int id)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
getString(@tringRes int id, Object... formatArgs)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      */
getQuantityString(@luralsRes int id, int quantity, Object... formatArgs)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      */
getQuantityString(@luralsRes int id, int quantity)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      */
getText(@tringRes int id, CharSequence def)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      */
getTextArray(@rrayRes int id)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      */
getStringArray(@rrayRes int id)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      */
getIntArray(@rrayRes int id)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      */
obtainTypedArray(@rrayRes int id)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      */
getDimension(@imenRes int id)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      */
getDimensionPixelOffset(@imenRes int id)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      */
getDimensionPixelSize(@imenRes int id)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      */
getFraction(@ractionRes int id, int base, int pbase)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
getDrawable(@rawableRes int id)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
getDrawable(@rawableRes int id, @Nullable Theme theme)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
getDrawableForDensity(@rawableRes int id, int density)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
getDrawableForDensity(@rawableRes int id, int density, @Nullable Theme theme)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      */
getMovie(@awRes int id)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
getColor(@olorRes int id)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
getColor(@olorRes int id, @Nullable Theme theme)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
getColorStateList(@olorRes int id)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
getColorStateList(@olorRes int id, @Nullable Theme theme)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      */
getBoolean(@oolRes int id)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      */
getInteger(@ntegerRes int id)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      */
getFloat(int id)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      */
getLayout(@ayoutRes int id)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      */
getAnimation(@nimRes int id)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      */
getXml(@mlRes int id)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      */
openRawResource(@awRes int id)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      */
openRawResource(@awRes int id, TypedValue value)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      */
openRawResourceFd(@awRes int id)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      */
getValue(@nyRes int id, TypedValue outValue, boolean resolveRefs)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      */
getValueForDensity(@nyRes int id, int density, TypedValue outValue, boolean resolveRefs)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      */
getValue(String name, TypedValue outValue, boolean resolveRefs)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          */
applyStyle(int resId, boolean force)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          */
setTo(Theme other)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          */
obtainStyledAttributes(@tyleableRes int[] attrs)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          */
obtainStyledAttributes(@tyleRes int resid, @StyleableRes int[] attrs)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          */
obtainStyledAttributes(AttributeSet set, @StyleableRes int[] attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes)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
resolveAttributes(@onNull int[] values, @NonNull int[] attrs)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          */
resolveAttribute(int resid, TypedValue outValue, boolean resolveRefs)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          */
getAllAttributes()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          */
getResources()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          */
getDrawable(@rawableRes int id)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          */
getChangingConfigurations()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          */
dump(int priority, String tag, String prefix)1753         public void dump(int priority, String tag, String prefix) {
1754             AssetManager.dumpTheme(mTheme, priority, tag, prefix);
1755         }
1756 
1757         @Override
finalize()1758         protected void finalize() throws Throwable {
1759             super.finalize();
1760             mAssets.releaseTheme(mTheme);
1761         }
1762 
Theme()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.
getNativeTheme()1779         /*package*/ long getNativeTheme() {
1780             return mTheme;
1781         }
1782 
getAppliedStyleResId()1783         /*package*/ int getAppliedStyleResId() {
1784             return mThemeResId;
1785         }
1786 
getKey()1787         /*package*/ ThemeKey getKey() {
1788             return mKey;
1789         }
1790 
getResourceNameFromHexString(String hexString)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)
getTheme()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 */
encode(@onNull ViewHierarchyEncoder encoder)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          */
rebase()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 
append(int resId, boolean force)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          */
setTo(ThemeKey other)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
hashCode()1883         public int hashCode() {
1884             return mHashCode;
1885         }
1886 
1887         @Override
equals(Object o)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
clone()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      */
newTheme()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      */
obtainAttributes(AttributeSet set, int[] attrs)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      */
updateConfiguration(Configuration config, DisplayMetrics metrics)1968     public void updateConfiguration(Configuration config,
1969             DisplayMetrics metrics) {
1970         updateConfiguration(config, metrics, null);
1971     }
1972 
1973     /**
1974      * @hide
1975      */
updateConfiguration(Configuration config, DisplayMetrics metrics, CompatibilityInfo compat)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      */
calcConfigChanges(Configuration config)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      */
adjustLanguageTag(String languageTag)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      */
updateSystemConfiguration(Configuration config, DisplayMetrics metrics, CompatibilityInfo compat)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      */
getDisplayMetrics()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      */
getConfiguration()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      */
getCompatibilityInfo()2162     public CompatibilityInfo getCompatibilityInfo() {
2163         return mCompatibilityInfo;
2164     }
2165 
2166     /**
2167      * This is just for testing.
2168      * @hide
2169      */
setCompatibilityInfo(CompatibilityInfo ci)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      */
getIdentifier(String name, String defType, String defPackage)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      */
resourceHasPackage(@nyRes int resid)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      */
getResourceName(@nyRes int resid)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      */
getResourcePackageName(@nyRes int resid)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      */
getResourceTypeName(@nyRes int resid)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      */
getResourceEntryName(@nyRes int resid)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      */
parseBundleExtras(XmlResourceParser parser, Bundle outBundle)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      */
parseBundleExtra(String tagName, AttributeSet attrs, Bundle outBundle)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      */
getAssets()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      */
flushLayoutCache()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      */
startPreloading()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      */
finishPreloading()2441     public final void finishPreloading() {
2442         if (mPreloading) {
2443             mPreloading = false;
2444             flushLayoutCache();
2445         }
2446     }
2447 
2448     /**
2449      * @hide
2450      */
getPreloadedDrawables()2451     public LongSparseArray<ConstantState> getPreloadedDrawables() {
2452         return sPreloadedDrawables[0];
2453     }
2454 
verifyPreloadConfig(int changingConfigurations, int allowVarying, int resourceId, String name)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
loadDrawable(TypedValue value, int id, Theme theme)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 
cacheDrawable(TypedValue value, boolean isColorDrawable, DrawableCache caches, Theme theme, boolean usesTheme, long key, Drawable dr)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      */
loadDrawableForCookie(TypedValue value, int id, Theme theme)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
loadColorStateList(TypedValue value, int id, Theme theme)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 
loadColorStateListForCookie(TypedValue value, int id, Theme theme)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 
loadXmlResourceParser(int id, String type)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 
loadXmlResourceParser(String file, int id, int assetCookie, String type)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      */
obtainAttributes( Resources res, Theme theme, AttributeSet set, int[] attrs)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 
Resources()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