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