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