1 /*
2  * Copyright (C) 2006 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.content.res;
18 
19 import android.animation.Animator;
20 import android.animation.StateListAnimator;
21 import android.annotation.AnimRes;
22 import android.annotation.AnimatorRes;
23 import android.annotation.AnyRes;
24 import android.annotation.ArrayRes;
25 import android.annotation.AttrRes;
26 import android.annotation.BoolRes;
27 import android.annotation.ColorInt;
28 import android.annotation.ColorRes;
29 import android.annotation.DimenRes;
30 import android.annotation.DrawableRes;
31 import android.annotation.FontRes;
32 import android.annotation.FractionRes;
33 import android.annotation.IntegerRes;
34 import android.annotation.LayoutRes;
35 import android.annotation.NonNull;
36 import android.annotation.Nullable;
37 import android.annotation.PluralsRes;
38 import android.annotation.RawRes;
39 import android.annotation.StringRes;
40 import android.annotation.StyleRes;
41 import android.annotation.StyleableRes;
42 import android.annotation.XmlRes;
43 import android.app.Application;
44 import android.compat.annotation.UnsupportedAppUsage;
45 import android.content.pm.ActivityInfo;
46 import android.content.pm.ActivityInfo.Config;
47 import android.content.res.loader.ResourcesLoader;
48 import android.graphics.Movie;
49 import android.graphics.Typeface;
50 import android.graphics.drawable.Drawable;
51 import android.graphics.drawable.Drawable.ConstantState;
52 import android.graphics.drawable.DrawableInflater;
53 import android.os.Build;
54 import android.os.Bundle;
55 import android.util.ArraySet;
56 import android.util.AttributeSet;
57 import android.util.DisplayMetrics;
58 import android.util.Log;
59 import android.util.LongSparseArray;
60 import android.util.Pools.SynchronizedPool;
61 import android.util.TypedValue;
62 import android.view.Display;
63 import android.view.DisplayAdjustments;
64 import android.view.ViewDebug;
65 import android.view.ViewHierarchyEncoder;
66 import android.view.WindowManager;
67 
68 import com.android.internal.annotations.GuardedBy;
69 import com.android.internal.annotations.VisibleForTesting;
70 import com.android.internal.util.ArrayUtils;
71 import com.android.internal.util.GrowingArrayUtils;
72 import com.android.internal.util.Preconditions;
73 import com.android.internal.util.XmlUtils;
74 
75 import org.xmlpull.v1.XmlPullParser;
76 import org.xmlpull.v1.XmlPullParserException;
77 
78 import java.io.IOException;
79 import java.io.InputStream;
80 import java.lang.ref.WeakReference;
81 import java.util.ArrayList;
82 import java.util.Collections;
83 import java.util.List;
84 import java.util.function.Consumer;
85 
86 /**
87  * Class for accessing an application's resources.  This sits on top of the
88  * asset manager of the application (accessible through {@link #getAssets}) and
89  * provides a high-level API for getting typed data from the assets.
90  *
91  * <p>The Android resource system keeps track of all non-code assets associated with an
92  * application. You can use this class to access your application's resources. You can generally
93  * acquire the {@link android.content.res.Resources} instance associated with your application
94  * with {@link android.content.Context#getResources getResources()}.</p>
95  *
96  * <p>The Android SDK tools compile your application's resources into the application binary
97  * at build time.  To use a resource, you must install it correctly in the source tree (inside
98  * your project's {@code res/} directory) and build your application.  As part of the build
99  * process, the SDK tools generate symbols for each resource, which you can use in your application
100  * code to access the resources.</p>
101  *
102  * <p>Using application resources makes it easy to update various characteristics of your
103  * application without modifying code, and&mdash;by providing sets of alternative
104  * resources&mdash;enables you to optimize your application for a variety of device configurations
105  * (such as for different languages and screen sizes). This is an important aspect of developing
106  * Android applications that are compatible on different types of devices.</p>
107  *
108  * <p>After {@link Build.VERSION_CODES#R}, {@link Resources} must be obtained by
109  * {@link android.app.Activity} or {@link android.content.Context} created with
110  * {@link android.content.Context#createWindowContext(int, Bundle)}.
111  * {@link Application#getResources()} may report wrong values in multi-window or on secondary
112  * displays.
113  *
114  * <p>For more information about using resources, see the documentation about <a
115  * href="{@docRoot}guide/topics/resources/index.html">Application Resources</a>.</p>
116  */
117 public class Resources {
118     /**
119      * The {@code null} resource ID. This denotes an invalid resource ID that is returned by the
120      * system when a resource is not found or the value is set to {@code @null} in XML.
121      */
122     public static final @AnyRes int ID_NULL = 0;
123 
124     static final String TAG = "Resources";
125 
126     private static final Object sSync = new Object();
127     private final Object mUpdateLock = new Object();
128 
129     // Used by BridgeResources in layoutlib
130     @UnsupportedAppUsage
131     static Resources mSystem = null;
132 
133     @UnsupportedAppUsage
134     private ResourcesImpl mResourcesImpl;
135 
136     // Pool of TypedArrays targeted to this Resources object.
137     @UnsupportedAppUsage
138     final SynchronizedPool<TypedArray> mTypedArrayPool = new SynchronizedPool<>(5);
139 
140     /** Used to inflate drawable objects from XML. */
141     @UnsupportedAppUsage
142     private DrawableInflater mDrawableInflater;
143 
144     /** Used to override the returned adjustments of {@link #getDisplayAdjustments}. */
145     private DisplayAdjustments mOverrideDisplayAdjustments;
146 
147     /** Lock object used to protect access to {@link #mTmpValue}. */
148     private final Object mTmpValueLock = new Object();
149 
150     /** Single-item pool used to minimize TypedValue allocations. */
151     @UnsupportedAppUsage
152     private TypedValue mTmpValue = new TypedValue();
153 
154     @UnsupportedAppUsage
155     final ClassLoader mClassLoader;
156 
157     @GuardedBy("mUpdateLock")
158     private UpdateCallbacks mCallbacks = null;
159 
160     /**
161      * WeakReferences to Themes that were constructed from this Resources object.
162      * We keep track of these in case our underlying implementation is changed, in which case
163      * the Themes must also get updated ThemeImpls.
164      */
165     private final ArrayList<WeakReference<Theme>> mThemeRefs = new ArrayList<>();
166 
167     /**
168      * To avoid leaking WeakReferences to garbage collected Themes on the
169      * mThemeRefs list, we flush the list of stale references any time the
170      * mThemeRefNextFlushSize is reached.
171      */
172     private static final int MIN_THEME_REFS_FLUSH_SIZE = 32;
173     private int mThemeRefsNextFlushSize = MIN_THEME_REFS_FLUSH_SIZE;
174 
175     private int mBaseApkAssetsSize;
176 
177     /**
178      * Returns the most appropriate default theme for the specified target SDK version.
179      * <ul>
180      * <li>Below API 11: Gingerbread
181      * <li>APIs 12 thru 14: Holo
182      * <li>APIs 15 thru 23: Device default dark
183      * <li>APIs 24 and above: Device default light with dark action bar
184      * </ul>
185      *
186      * @param curTheme The current theme, or 0 if not specified.
187      * @param targetSdkVersion The target SDK version.
188      * @return A theme resource identifier
189      * @hide
190      */
191     @UnsupportedAppUsage
selectDefaultTheme(int curTheme, int targetSdkVersion)192     public static int selectDefaultTheme(int curTheme, int targetSdkVersion) {
193         return selectSystemTheme(curTheme, targetSdkVersion,
194                 com.android.internal.R.style.Theme,
195                 com.android.internal.R.style.Theme_Holo,
196                 com.android.internal.R.style.Theme_DeviceDefault,
197                 com.android.internal.R.style.Theme_DeviceDefault_Light_DarkActionBar);
198     }
199 
200     /** @hide */
selectSystemTheme(int curTheme, int targetSdkVersion, int orig, int holo, int dark, int deviceDefault)201     public static int selectSystemTheme(int curTheme, int targetSdkVersion, int orig, int holo,
202             int dark, int deviceDefault) {
203         if (curTheme != ID_NULL) {
204             return curTheme;
205         }
206         if (targetSdkVersion < Build.VERSION_CODES.HONEYCOMB) {
207             return orig;
208         }
209         if (targetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
210             return holo;
211         }
212         if (targetSdkVersion < Build.VERSION_CODES.N) {
213             return dark;
214         }
215         return deviceDefault;
216     }
217 
218     /**
219      * Return a global shared Resources object that provides access to only
220      * system resources (no application resources), is not configured for the
221      * current screen (can not use dimension units, does not change based on
222      * orientation, etc), and is not affected by Runtime Resource Overlay.
223      */
getSystem()224     public static Resources getSystem() {
225         synchronized (sSync) {
226             Resources ret = mSystem;
227             if (ret == null) {
228                 ret = new Resources();
229                 mSystem = ret;
230             }
231             return ret;
232         }
233     }
234 
235     /**
236      * This exception is thrown by the resource APIs when a requested resource
237      * can not be found.
238      */
239     public static class NotFoundException extends RuntimeException {
NotFoundException()240         public NotFoundException() {
241         }
242 
NotFoundException(String name)243         public NotFoundException(String name) {
244             super(name);
245         }
246 
NotFoundException(String name, Exception cause)247         public NotFoundException(String name, Exception cause) {
248             super(name, cause);
249         }
250     }
251 
252     /** @hide */
253     public interface UpdateCallbacks extends ResourcesLoader.UpdateCallbacks {
254         /**
255          * Invoked when a {@link Resources} instance has a {@link ResourcesLoader} added, removed,
256          * or reordered.
257          *
258          * @param resources the instance being updated
259          * @param newLoaders the new set of loaders for the instance
260          */
onLoadersChanged(@onNull Resources resources, @NonNull List<ResourcesLoader> newLoaders)261         void onLoadersChanged(@NonNull Resources resources,
262                 @NonNull List<ResourcesLoader> newLoaders);
263     }
264 
265     /**
266      * Handler that propagates updates of the {@link Resources} instance to the underlying
267      * {@link AssetManager} when the Resources is not registered with a
268      * {@link android.app.ResourcesManager}.
269      * @hide
270      */
271     public class AssetManagerUpdateHandler implements UpdateCallbacks{
272 
273         @Override
onLoadersChanged(@onNull Resources resources, @NonNull List<ResourcesLoader> newLoaders)274         public void onLoadersChanged(@NonNull Resources resources,
275                 @NonNull List<ResourcesLoader> newLoaders) {
276             Preconditions.checkArgument(Resources.this == resources);
277             final ResourcesImpl impl = mResourcesImpl;
278             impl.clearAllCaches();
279             impl.getAssets().setLoaders(newLoaders);
280         }
281 
282         @Override
onLoaderUpdated(@onNull ResourcesLoader loader)283         public void onLoaderUpdated(@NonNull ResourcesLoader loader) {
284             final ResourcesImpl impl = mResourcesImpl;
285             final AssetManager assets = impl.getAssets();
286             if (assets.getLoaders().contains(loader)) {
287                 impl.clearAllCaches();
288                 assets.setLoaders(assets.getLoaders());
289             }
290         }
291     }
292 
293     /**
294      * Create a new Resources object on top of an existing set of assets in an
295      * AssetManager.
296      *
297      * @deprecated Resources should not be constructed by apps.
298      * See {@link android.content.Context#createConfigurationContext(Configuration)}.
299      *
300      * @param assets Previously created AssetManager.
301      * @param metrics Current display metrics to consider when
302      *                selecting/computing resource values.
303      * @param config Desired device configuration to consider when
304      *               selecting/computing resource values (optional).
305      */
306     @Deprecated
Resources(AssetManager assets, DisplayMetrics metrics, Configuration config)307     public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) {
308         this(null);
309         mResourcesImpl = new ResourcesImpl(assets, metrics, config, new DisplayAdjustments());
310     }
311 
312     /**
313      * Creates a new Resources object with CompatibilityInfo.
314      *
315      * @param classLoader class loader for the package used to load custom
316      *                    resource classes, may be {@code null} to use system
317      *                    class loader
318      * @hide
319      */
320     @UnsupportedAppUsage
Resources(@ullable ClassLoader classLoader)321     public Resources(@Nullable ClassLoader classLoader) {
322         mClassLoader = classLoader == null ? ClassLoader.getSystemClassLoader() : classLoader;
323     }
324 
325     /**
326      * Only for creating the System resources.
327      */
328     @UnsupportedAppUsage
Resources()329     private Resources() {
330         this(null);
331 
332         final DisplayMetrics metrics = new DisplayMetrics();
333         metrics.setToDefaults();
334 
335         final Configuration config = new Configuration();
336         config.setToDefaults();
337 
338         mResourcesImpl = new ResourcesImpl(AssetManager.getSystem(), metrics, config,
339                 new DisplayAdjustments());
340     }
341 
342     /**
343      * Set the underlying implementation (containing all the resources and caches)
344      * and updates all Theme references to new implementations as well.
345      * @hide
346      */
347     @UnsupportedAppUsage
setImpl(ResourcesImpl impl)348     public void setImpl(ResourcesImpl impl) {
349         if (impl == mResourcesImpl) {
350             return;
351         }
352 
353         mBaseApkAssetsSize = ArrayUtils.size(impl.getAssets().getApkAssets());
354         mResourcesImpl = impl;
355 
356         // Create new ThemeImpls that are identical to the ones we have.
357         synchronized (mThemeRefs) {
358             final int count = mThemeRefs.size();
359             for (int i = 0; i < count; i++) {
360                 WeakReference<Theme> weakThemeRef = mThemeRefs.get(i);
361                 Theme theme = weakThemeRef != null ? weakThemeRef.get() : null;
362                 if (theme != null) {
363                     theme.setImpl(mResourcesImpl.newThemeImpl(theme.getKey()));
364                 }
365             }
366         }
367     }
368 
369     /** @hide */
setCallbacks(UpdateCallbacks callbacks)370     public void setCallbacks(UpdateCallbacks callbacks) {
371         if (mCallbacks != null) {
372             throw new IllegalStateException("callback already registered");
373         }
374 
375         mCallbacks = callbacks;
376     }
377 
378     /**
379      * @hide
380      */
381     @UnsupportedAppUsage
getImpl()382     public ResourcesImpl getImpl() {
383         return mResourcesImpl;
384     }
385 
386     /**
387      * @hide
388      */
getClassLoader()389     public ClassLoader getClassLoader() {
390         return mClassLoader;
391     }
392 
393     /**
394      * @return the inflater used to create drawable objects
395      * @hide Pending API finalization.
396      */
397     @UnsupportedAppUsage
getDrawableInflater()398     public final DrawableInflater getDrawableInflater() {
399         if (mDrawableInflater == null) {
400             mDrawableInflater = new DrawableInflater(this, mClassLoader);
401         }
402         return mDrawableInflater;
403     }
404 
405     /**
406      * Used by AnimatorInflater.
407      *
408      * @hide
409      */
getAnimatorCache()410     public ConfigurationBoundResourceCache<Animator> getAnimatorCache() {
411         return mResourcesImpl.getAnimatorCache();
412     }
413 
414     /**
415      * Used by AnimatorInflater.
416      *
417      * @hide
418      */
getStateListAnimatorCache()419     public ConfigurationBoundResourceCache<StateListAnimator> getStateListAnimatorCache() {
420         return mResourcesImpl.getStateListAnimatorCache();
421     }
422 
423     /**
424      * Return the string value associated with a particular resource ID.  The
425      * returned object will be a String if this is a plain string; it will be
426      * some other type of CharSequence if it is styled.
427      * {@more}
428      *
429      * @param id The desired resource identifier, as generated by the aapt
430      *           tool. This integer encodes the package, type, and resource
431      *           entry. The value 0 is an invalid identifier.
432      *
433      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
434      *
435      * @return CharSequence The string data associated with the resource, plus
436      *         possibly styled text information.
437      */
getText(@tringRes int id)438     @NonNull public CharSequence getText(@StringRes int id) throws NotFoundException {
439         CharSequence res = mResourcesImpl.getAssets().getResourceText(id);
440         if (res != null) {
441             return res;
442         }
443         throw new NotFoundException("String resource ID #0x"
444                 + Integer.toHexString(id));
445     }
446 
447     /**
448      * Return the Typeface value associated with a particular resource ID.
449      * {@more}
450      *
451      * @param id The desired resource identifier, as generated by the aapt
452      *           tool. This integer encodes the package, type, and resource
453      *           entry. The value 0 is an invalid identifier.
454      *
455      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
456      *
457      * @return Typeface The Typeface data associated with the resource.
458      */
getFont(@ontRes int id)459     @NonNull public Typeface getFont(@FontRes int id) throws NotFoundException {
460         final TypedValue value = obtainTempTypedValue();
461         try {
462             final ResourcesImpl impl = mResourcesImpl;
463             impl.getValue(id, value, true);
464             Typeface typeface = impl.loadFont(this, value, id);
465             if (typeface != null) {
466                 return typeface;
467             }
468         } finally {
469             releaseTempTypedValue(value);
470         }
471         throw new NotFoundException("Font resource ID #0x"
472                 + Integer.toHexString(id));
473     }
474 
475     @NonNull
getFont(@onNull TypedValue value, @FontRes int id)476     Typeface getFont(@NonNull TypedValue value, @FontRes int id) throws NotFoundException {
477         return mResourcesImpl.loadFont(this, value, id);
478     }
479 
480     /**
481      * @hide
482      */
preloadFonts(@rrayRes int id)483     public void preloadFonts(@ArrayRes int id) {
484         final TypedArray array = obtainTypedArray(id);
485         try {
486             final int size = array.length();
487             for (int i = 0; i < size; i++) {
488                 array.getFont(i);
489             }
490         } finally {
491             array.recycle();
492         }
493     }
494 
495     /**
496      * Returns the character sequence necessary for grammatically correct pluralization
497      * of the given resource ID for the given quantity.
498      * Note that the character sequence is selected based solely on grammatical necessity,
499      * and that such rules differ between languages. Do not assume you know which string
500      * will be returned for a given quantity. See
501      * <a href="{@docRoot}guide/topics/resources/string-resource.html#Plurals">String Resources</a>
502      * for more detail.
503      *
504      * @param id The desired resource identifier, as generated by the aapt
505      *           tool. This integer encodes the package, type, and resource
506      *           entry. The value 0 is an invalid identifier.
507      * @param quantity The number used to get the correct string for the current language's
508      *           plural rules.
509      *
510      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
511      *
512      * @return CharSequence The string data associated with the resource, plus
513      *         possibly styled text information.
514      */
515     @NonNull
getQuantityText(@luralsRes int id, int quantity)516     public CharSequence getQuantityText(@PluralsRes int id, int quantity)
517             throws NotFoundException {
518         return mResourcesImpl.getQuantityText(id, quantity);
519     }
520 
521     /**
522      * Return the string value associated with a particular resource ID.  It
523      * will be stripped of any styled text information.
524      * {@more}
525      *
526      * @param id The desired resource identifier, as generated by the aapt
527      *           tool. This integer encodes the package, type, and resource
528      *           entry. The value 0 is an invalid identifier.
529      *
530      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
531      *
532      * @return String The string data associated with the resource,
533      *         stripped of styled text information.
534      */
535     @NonNull
getString(@tringRes int id)536     public String getString(@StringRes int id) throws NotFoundException {
537         return getText(id).toString();
538     }
539 
540 
541     /**
542      * Return the string value associated with a particular resource ID,
543      * substituting the format arguments as defined in {@link java.util.Formatter}
544      * and {@link java.lang.String#format}. It will be stripped of any styled text
545      * information.
546      * {@more}
547      *
548      * @param id The desired resource identifier, as generated by the aapt
549      *           tool. This integer encodes the package, type, and resource
550      *           entry. The value 0 is an invalid identifier.
551      *
552      * @param formatArgs The format arguments that will be used for substitution.
553      *
554      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
555      *
556      * @return String The string data associated with the resource,
557      *         stripped of styled text information.
558      */
559     @NonNull
getString(@tringRes int id, Object... formatArgs)560     public String getString(@StringRes int id, Object... formatArgs) throws NotFoundException {
561         final String raw = getString(id);
562         return String.format(mResourcesImpl.getConfiguration().getLocales().get(0), raw,
563                 formatArgs);
564     }
565 
566     /**
567      * Formats the string necessary for grammatically correct pluralization
568      * of the given resource ID for the given quantity, using the given arguments.
569      * Note that the string is selected based solely on grammatical necessity,
570      * and that such rules differ between languages. Do not assume you know which string
571      * will be returned for a given quantity. See
572      * <a href="{@docRoot}guide/topics/resources/string-resource.html#Plurals">String Resources</a>
573      * for more detail.
574      *
575      * <p>Substitution of format arguments works as if using
576      * {@link java.util.Formatter} and {@link java.lang.String#format}.
577      * The resulting string will be stripped of any styled text information.
578      *
579      * @param id The desired resource identifier, as generated by the aapt
580      *           tool. This integer encodes the package, type, and resource
581      *           entry. The value 0 is an invalid identifier.
582      * @param quantity The number used to get the correct string for the current language's
583      *           plural rules.
584      * @param formatArgs The format arguments that will be used for substitution.
585      *
586      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
587      *
588      * @return String The string data associated with the resource,
589      * stripped of styled text information.
590      */
591     @NonNull
getQuantityString(@luralsRes int id, int quantity, Object... formatArgs)592     public String getQuantityString(@PluralsRes int id, int quantity, Object... formatArgs)
593             throws NotFoundException {
594         String raw = getQuantityText(id, quantity).toString();
595         return String.format(mResourcesImpl.getConfiguration().getLocales().get(0), raw,
596                 formatArgs);
597     }
598 
599     /**
600      * Returns the string necessary for grammatically correct pluralization
601      * of the given resource ID for the given quantity.
602      * Note that the string is selected based solely on grammatical necessity,
603      * and that such rules differ between languages. Do not assume you know which string
604      * will be returned for a given quantity. See
605      * <a href="{@docRoot}guide/topics/resources/string-resource.html#Plurals">String Resources</a>
606      * for more detail.
607      *
608      * @param id The desired resource identifier, as generated by the aapt
609      *           tool. This integer encodes the package, type, and resource
610      *           entry. The value 0 is an invalid identifier.
611      * @param quantity The number used to get the correct string for the current language's
612      *           plural rules.
613      *
614      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
615      *
616      * @return String The string data associated with the resource,
617      * stripped of styled text information.
618      */
619     @NonNull
getQuantityString(@luralsRes int id, int quantity)620     public String getQuantityString(@PluralsRes int id, int quantity) throws NotFoundException {
621         return getQuantityText(id, quantity).toString();
622     }
623 
624     /**
625      * Return the string value associated with a particular resource ID.  The
626      * returned object will be a String if this is a plain string; it will be
627      * some other type of CharSequence if it is styled.
628      *
629      * @param id The desired resource identifier, as generated by the aapt
630      *           tool. This integer encodes the package, type, and resource
631      *           entry. The value 0 is an invalid identifier.
632      *
633      * @param def The default CharSequence to return.
634      *
635      * @return CharSequence The string data associated with the resource, plus
636      *         possibly styled text information, or def if id is 0 or not found.
637      */
getText(@tringRes int id, CharSequence def)638     public CharSequence getText(@StringRes int id, CharSequence def) {
639         CharSequence res = id != 0 ? mResourcesImpl.getAssets().getResourceText(id) : null;
640         return res != null ? res : def;
641     }
642 
643     /**
644      * Return the styled text array associated with a particular resource ID.
645      *
646      * @param id The desired resource identifier, as generated by the aapt
647      *           tool. This integer encodes the package, type, and resource
648      *           entry. The value 0 is an invalid identifier.
649      *
650      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
651      *
652      * @return The styled text array associated with the resource.
653      */
654     @NonNull
getTextArray(@rrayRes int id)655     public CharSequence[] getTextArray(@ArrayRes int id) throws NotFoundException {
656         CharSequence[] res = mResourcesImpl.getAssets().getResourceTextArray(id);
657         if (res != null) {
658             return res;
659         }
660         throw new NotFoundException("Text array resource ID #0x" + Integer.toHexString(id));
661     }
662 
663     /**
664      * Return the string array associated with a particular resource ID.
665      *
666      * @param id The desired resource identifier, as generated by the aapt
667      *           tool. This integer encodes the package, type, and resource
668      *           entry. The value 0 is an invalid identifier.
669      *
670      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
671      *
672      * @return The string array associated with the resource.
673      */
674     @NonNull
getStringArray(@rrayRes int id)675     public String[] getStringArray(@ArrayRes int id)
676             throws NotFoundException {
677         String[] res = mResourcesImpl.getAssets().getResourceStringArray(id);
678         if (res != null) {
679             return res;
680         }
681         throw new NotFoundException("String array resource ID #0x" + Integer.toHexString(id));
682     }
683 
684     /**
685      * Return the int array associated with a particular resource ID.
686      *
687      * @param id The desired resource identifier, as generated by the aapt
688      *           tool. This integer encodes the package, type, and resource
689      *           entry. The value 0 is an invalid identifier.
690      *
691      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
692      *
693      * @return The int array associated with the resource.
694      */
695     @NonNull
getIntArray(@rrayRes int id)696     public int[] getIntArray(@ArrayRes int id) throws NotFoundException {
697         int[] res = mResourcesImpl.getAssets().getResourceIntArray(id);
698         if (res != null) {
699             return res;
700         }
701         throw new NotFoundException("Int array resource ID #0x" + Integer.toHexString(id));
702     }
703 
704     /**
705      * Return an array of heterogeneous values.
706      *
707      * @param id The desired resource identifier, as generated by the aapt
708      *           tool. This integer encodes the package, type, and resource
709      *           entry. The value 0 is an invalid identifier.
710      *
711      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
712      *
713      * @return Returns a TypedArray holding an array of the array values.
714      * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
715      * when done with it.
716      */
717     @NonNull
obtainTypedArray(@rrayRes int id)718     public TypedArray obtainTypedArray(@ArrayRes int id) throws NotFoundException {
719         final ResourcesImpl impl = mResourcesImpl;
720         int len = impl.getAssets().getResourceArraySize(id);
721         if (len < 0) {
722             throw new NotFoundException("Array resource ID #0x" + Integer.toHexString(id));
723         }
724 
725         TypedArray array = TypedArray.obtain(this, len);
726         array.mLength = impl.getAssets().getResourceArray(id, array.mData);
727         array.mIndices[0] = 0;
728 
729         return array;
730     }
731 
732     /**
733      * Retrieve a dimensional for a particular resource ID.  Unit
734      * conversions are based on the current {@link DisplayMetrics} associated
735      * with the resources.
736      *
737      * @param id The desired resource identifier, as generated by the aapt
738      *           tool. This integer encodes the package, type, and resource
739      *           entry. The value 0 is an invalid identifier.
740      *
741      * @return Resource dimension value multiplied by the appropriate metric to convert to pixels.
742      *
743      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
744      *
745      * @see #getDimensionPixelOffset
746      * @see #getDimensionPixelSize
747      */
getDimension(@imenRes int id)748     public float getDimension(@DimenRes int id) throws NotFoundException {
749         final TypedValue value = obtainTempTypedValue();
750         try {
751             final ResourcesImpl impl = mResourcesImpl;
752             impl.getValue(id, value, true);
753             if (value.type == TypedValue.TYPE_DIMENSION) {
754                 return TypedValue.complexToDimension(value.data, impl.getDisplayMetrics());
755             }
756             throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
757                     + " type #0x" + Integer.toHexString(value.type) + " is not valid");
758         } finally {
759             releaseTempTypedValue(value);
760         }
761     }
762 
763     /**
764      * Retrieve a dimensional for a particular resource ID for use
765      * as an offset in raw pixels.  This is the same as
766      * {@link #getDimension}, except the returned value is converted to
767      * integer pixels for you.  An offset conversion involves simply
768      * truncating the base value to an integer.
769      *
770      * @param id The desired resource identifier, as generated by the aapt
771      *           tool. This integer encodes the package, type, and resource
772      *           entry. The value 0 is an invalid identifier.
773      *
774      * @return Resource dimension value multiplied by the appropriate
775      * metric and truncated to integer pixels.
776      *
777      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
778      *
779      * @see #getDimension
780      * @see #getDimensionPixelSize
781      */
getDimensionPixelOffset(@imenRes int id)782     public int getDimensionPixelOffset(@DimenRes int id) throws NotFoundException {
783         final TypedValue value = obtainTempTypedValue();
784         try {
785             final ResourcesImpl impl = mResourcesImpl;
786             impl.getValue(id, value, true);
787             if (value.type == TypedValue.TYPE_DIMENSION) {
788                 return TypedValue.complexToDimensionPixelOffset(value.data,
789                         impl.getDisplayMetrics());
790             }
791             throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
792                     + " type #0x" + Integer.toHexString(value.type) + " is not valid");
793         } finally {
794             releaseTempTypedValue(value);
795         }
796     }
797 
798     /**
799      * Retrieve a dimensional for a particular resource ID for use
800      * as a size in raw pixels.  This is the same as
801      * {@link #getDimension}, except the returned value is converted to
802      * integer pixels for use as a size.  A size conversion involves
803      * rounding the base value, and ensuring that a non-zero base value
804      * is at least one pixel in size.
805      *
806      * @param id The desired resource identifier, as generated by the aapt
807      *           tool. This integer encodes the package, type, and resource
808      *           entry. The value 0 is an invalid identifier.
809      *
810      * @return Resource dimension value multiplied by the appropriate
811      * metric and truncated to integer pixels.
812      *
813      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
814      *
815      * @see #getDimension
816      * @see #getDimensionPixelOffset
817      */
getDimensionPixelSize(@imenRes int id)818     public int getDimensionPixelSize(@DimenRes int id) throws NotFoundException {
819         final TypedValue value = obtainTempTypedValue();
820         try {
821             final ResourcesImpl impl = mResourcesImpl;
822             impl.getValue(id, value, true);
823             if (value.type == TypedValue.TYPE_DIMENSION) {
824                 return TypedValue.complexToDimensionPixelSize(value.data, impl.getDisplayMetrics());
825             }
826             throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
827                     + " type #0x" + Integer.toHexString(value.type) + " is not valid");
828         } finally {
829             releaseTempTypedValue(value);
830         }
831     }
832 
833     /**
834      * Retrieve a fractional unit for a particular resource ID.
835      *
836      * @param id The desired resource identifier, as generated by the aapt
837      *           tool. This integer encodes the package, type, and resource
838      *           entry. The value 0 is an invalid identifier.
839      * @param base The base value of this fraction.  In other words, a
840      *             standard fraction is multiplied by this value.
841      * @param pbase The parent base value of this fraction.  In other
842      *             words, a parent fraction (nn%p) is multiplied by this
843      *             value.
844      *
845      * @return Attribute fractional value multiplied by the appropriate
846      * base value.
847      *
848      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
849      */
getFraction(@ractionRes int id, int base, int pbase)850     public float getFraction(@FractionRes int id, int base, int pbase) {
851         final TypedValue value = obtainTempTypedValue();
852         try {
853             mResourcesImpl.getValue(id, value, true);
854             if (value.type == TypedValue.TYPE_FRACTION) {
855                 return TypedValue.complexToFraction(value.data, base, pbase);
856             }
857             throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
858                     + " type #0x" + Integer.toHexString(value.type) + " is not valid");
859         } finally {
860             releaseTempTypedValue(value);
861         }
862     }
863 
864     /**
865      * Return a drawable object associated with a particular resource ID.
866      * Various types of objects will be returned depending on the underlying
867      * resource -- for example, a solid color, PNG image, scalable image, etc.
868      * The Drawable API hides these implementation details.
869      *
870      * <p class="note"><strong>Note:</strong> Prior to
871      * {@link android.os.Build.VERSION_CODES#JELLY_BEAN}, this function
872      * would not correctly retrieve the final configuration density when
873      * the resource ID passed here is an alias to another Drawable resource.
874      * This means that if the density configuration of the alias resource
875      * is different than the actual resource, the density of the returned
876      * Drawable would be incorrect, resulting in bad scaling. To work
877      * around this, you can instead manually resolve the aliased reference
878      * by using {@link #getValue(int, TypedValue, boolean)} and passing
879      * {@code true} for {@code resolveRefs}. The resulting
880      * {@link TypedValue#resourceId} value may be passed to this method.</p>
881      *
882      * <p class="note"><strong>Note:</strong> To obtain a themed drawable, use
883      * {@link android.content.Context#getDrawable(int) Context.getDrawable(int)}
884      * or {@link #getDrawable(int, Theme)} passing the desired theme.</p>
885      *
886      * @param id The desired resource identifier, as generated by the aapt
887      *           tool. This integer encodes the package, type, and resource
888      *           entry. The value 0 is an invalid identifier.
889      * @return Drawable An object that can be used to draw this resource.
890      * @throws NotFoundException Throws NotFoundException if the given ID does
891      *         not exist.
892      * @see #getDrawable(int, Theme)
893      * @deprecated Use {@link #getDrawable(int, Theme)} instead.
894      */
895     @Deprecated
getDrawable(@rawableRes int id)896     public Drawable getDrawable(@DrawableRes int id) throws NotFoundException {
897         final Drawable d = getDrawable(id, null);
898         if (d != null && d.canApplyTheme()) {
899             Log.w(TAG, "Drawable " + getResourceName(id) + " has unresolved theme "
900                     + "attributes! Consider using Resources.getDrawable(int, Theme) or "
901                     + "Context.getDrawable(int).", new RuntimeException());
902         }
903         return d;
904     }
905 
906     /**
907      * Return a drawable object associated with a particular resource ID and
908      * styled for the specified theme. Various types of objects will be
909      * returned depending on the underlying resource -- for example, a solid
910      * color, PNG image, scalable image, etc.
911      *
912      * @param id The desired resource identifier, as generated by the aapt
913      *           tool. This integer encodes the package, type, and resource
914      *           entry. The value 0 is an invalid identifier.
915      * @param theme The theme used to style the drawable attributes, may be {@code null}.
916      * @return Drawable An object that can be used to draw this resource.
917      * @throws NotFoundException Throws NotFoundException if the given ID does
918      *         not exist.
919      */
getDrawable(@rawableRes int id, @Nullable Theme theme)920     public Drawable getDrawable(@DrawableRes int id, @Nullable Theme theme)
921             throws NotFoundException {
922         return getDrawableForDensity(id, 0, theme);
923     }
924 
925     /**
926      * Return a drawable object associated with a particular resource ID for the
927      * given screen density in DPI. This will set the drawable's density to be
928      * the device's density multiplied by the ratio of actual drawable density
929      * to requested density. This allows the drawable to be scaled up to the
930      * correct size if needed. Various types of objects will be returned
931      * depending on the underlying resource -- for example, a solid color, PNG
932      * image, scalable image, etc. The Drawable API hides these implementation
933      * details.
934      *
935      * <p class="note"><strong>Note:</strong> To obtain a themed drawable, use
936      * {@link android.content.Context#getDrawable(int) Context.getDrawable(int)}
937      * or {@link #getDrawableForDensity(int, int, Theme)} passing the desired
938      * theme.</p>
939      *
940      * @param id The desired resource identifier, as generated by the aapt tool.
941      *            This integer encodes the package, type, and resource entry.
942      *            The value 0 is an invalid identifier.
943      * @param density the desired screen density indicated by the resource as
944      *            found in {@link DisplayMetrics}. A value of 0 means to use the
945      *            density returned from {@link #getConfiguration()}.
946      *            This is equivalent to calling {@link #getDrawable(int)}.
947      * @return Drawable An object that can be used to draw this resource.
948      * @throws NotFoundException Throws NotFoundException if the given ID does
949      *             not exist.
950      * @see #getDrawableForDensity(int, int, Theme)
951      * @deprecated Use {@link #getDrawableForDensity(int, int, Theme)} instead.
952      */
953     @Nullable
954     @Deprecated
getDrawableForDensity(@rawableRes int id, int density)955     public Drawable getDrawableForDensity(@DrawableRes int id, int density)
956             throws NotFoundException {
957         return getDrawableForDensity(id, density, null);
958     }
959 
960     /**
961      * Return a drawable object associated with a particular resource ID for the
962      * given screen density in DPI and styled for the specified theme.
963      *
964      * @param id The desired resource identifier, as generated by the aapt tool.
965      *            This integer encodes the package, type, and resource entry.
966      *            The value 0 is an invalid identifier.
967      * @param density The desired screen density indicated by the resource as
968      *            found in {@link DisplayMetrics}. A value of 0 means to use the
969      *            density returned from {@link #getConfiguration()}.
970      *            This is equivalent to calling {@link #getDrawable(int, Theme)}.
971      * @param theme The theme used to style the drawable attributes, may be {@code null} if the
972      *              drawable cannot be decoded.
973      * @return Drawable An object that can be used to draw this resource.
974      * @throws NotFoundException Throws NotFoundException if the given ID does
975      *             not exist.
976      */
977     @Nullable
getDrawableForDensity(@rawableRes int id, int density, @Nullable Theme theme)978     public Drawable getDrawableForDensity(@DrawableRes int id, int density, @Nullable Theme theme) {
979         final TypedValue value = obtainTempTypedValue();
980         try {
981             final ResourcesImpl impl = mResourcesImpl;
982             impl.getValueForDensity(id, density, value, true);
983             return loadDrawable(value, id, density, theme);
984         } finally {
985             releaseTempTypedValue(value);
986         }
987     }
988 
989     @NonNull
990     @UnsupportedAppUsage
loadDrawable(@onNull TypedValue value, int id, int density, @Nullable Theme theme)991     Drawable loadDrawable(@NonNull TypedValue value, int id, int density, @Nullable Theme theme)
992             throws NotFoundException {
993         return mResourcesImpl.loadDrawable(this, value, id, density, theme);
994     }
995 
996     /**
997      * Return a movie object associated with the particular resource ID.
998      * @param id The desired resource identifier, as generated by the aapt
999      *           tool. This integer encodes the package, type, and resource
1000      *           entry. The value 0 is an invalid identifier.
1001      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1002      *
1003      * @deprecated Prefer {@link android.graphics.drawable.AnimatedImageDrawable}.
1004      */
1005     @Deprecated
getMovie(@awRes int id)1006     public Movie getMovie(@RawRes int id) throws NotFoundException {
1007         final InputStream is = openRawResource(id);
1008         final Movie movie = Movie.decodeStream(is);
1009         try {
1010             is.close();
1011         } catch (IOException e) {
1012             // No one cares.
1013         }
1014         return movie;
1015     }
1016 
1017     /**
1018      * Returns a color integer associated with a particular resource ID. If the
1019      * resource holds a complex {@link ColorStateList}, then the default color
1020      * from the set is returned.
1021      *
1022      * @param id The desired resource identifier, as generated by the aapt
1023      *           tool. This integer encodes the package, type, and resource
1024      *           entry. The value 0 is an invalid identifier.
1025      *
1026      * @throws NotFoundException Throws NotFoundException if the given ID does
1027      *         not exist.
1028      *
1029      * @return A single color value in the form 0xAARRGGBB.
1030      * @deprecated Use {@link #getColor(int, Theme)} instead.
1031      */
1032     @ColorInt
1033     @Deprecated
getColor(@olorRes int id)1034     public int getColor(@ColorRes int id) throws NotFoundException {
1035         return getColor(id, null);
1036     }
1037 
1038     /**
1039      * Returns a themed color integer associated with a particular resource ID.
1040      * If the resource holds a complex {@link ColorStateList}, then the default
1041      * color from the set is returned.
1042      *
1043      * @param id The desired resource identifier, as generated by the aapt
1044      *           tool. This integer encodes the package, type, and resource
1045      *           entry. The value 0 is an invalid identifier.
1046      * @param theme The theme used to style the color attributes, may be
1047      *              {@code null}.
1048      *
1049      * @throws NotFoundException Throws NotFoundException if the given ID does
1050      *         not exist.
1051      *
1052      * @return A single color value in the form 0xAARRGGBB.
1053      */
1054     @ColorInt
getColor(@olorRes int id, @Nullable Theme theme)1055     public int getColor(@ColorRes int id, @Nullable Theme theme) throws NotFoundException {
1056         final TypedValue value = obtainTempTypedValue();
1057         try {
1058             final ResourcesImpl impl = mResourcesImpl;
1059             impl.getValue(id, value, true);
1060             if (value.type >= TypedValue.TYPE_FIRST_INT
1061                     && value.type <= TypedValue.TYPE_LAST_INT) {
1062                 return value.data;
1063             } else if (value.type != TypedValue.TYPE_STRING) {
1064                 throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
1065                         + " type #0x" + Integer.toHexString(value.type) + " is not valid");
1066             }
1067 
1068             final ColorStateList csl = impl.loadColorStateList(this, value, id, theme);
1069             return csl.getDefaultColor();
1070         } finally {
1071             releaseTempTypedValue(value);
1072         }
1073     }
1074 
1075     /**
1076      * Returns a color state list associated with a particular resource ID. The
1077      * resource may contain either a single raw color value or a complex
1078      * {@link ColorStateList} holding multiple possible colors.
1079      *
1080      * @param id The desired resource identifier of a {@link ColorStateList},
1081      *           as generated by the aapt tool. This integer encodes the
1082      *           package, type, and resource entry. The value 0 is an invalid
1083      *           identifier.
1084      *
1085      * @throws NotFoundException Throws NotFoundException if the given ID does
1086      *         not exist.
1087      *
1088      * @return A ColorStateList object containing either a single solid color
1089      *         or multiple colors that can be selected based on a state.
1090      * @deprecated Use {@link #getColorStateList(int, Theme)} instead.
1091      */
1092     @NonNull
1093     @Deprecated
getColorStateList(@olorRes int id)1094     public ColorStateList getColorStateList(@ColorRes int id) throws NotFoundException {
1095         final ColorStateList csl = getColorStateList(id, null);
1096         if (csl != null && csl.canApplyTheme()) {
1097             Log.w(TAG, "ColorStateList " + getResourceName(id) + " has "
1098                     + "unresolved theme attributes! Consider using "
1099                     + "Resources.getColorStateList(int, Theme) or "
1100                     + "Context.getColorStateList(int).", new RuntimeException());
1101         }
1102         return csl;
1103     }
1104 
1105     /**
1106      * Returns a themed color state list associated with a particular resource
1107      * ID. The resource may contain either a single raw color value or a
1108      * complex {@link ColorStateList} holding multiple possible colors.
1109      *
1110      * @param id The desired resource identifier of a {@link ColorStateList},
1111      *           as generated by the aapt tool. This integer encodes the
1112      *           package, type, and resource entry. The value 0 is an invalid
1113      *           identifier.
1114      * @param theme The theme used to style the color attributes, may be
1115      *              {@code null}.
1116      *
1117      * @throws NotFoundException Throws NotFoundException if the given ID does
1118      *         not exist.
1119      *
1120      * @return A themed ColorStateList object containing either a single solid
1121      *         color or multiple colors that can be selected based on a state.
1122      */
1123     @NonNull
getColorStateList(@olorRes int id, @Nullable Theme theme)1124     public ColorStateList getColorStateList(@ColorRes int id, @Nullable Theme theme)
1125             throws NotFoundException {
1126         final TypedValue value = obtainTempTypedValue();
1127         try {
1128             final ResourcesImpl impl = mResourcesImpl;
1129             impl.getValue(id, value, true);
1130             return impl.loadColorStateList(this, value, id, theme);
1131         } finally {
1132             releaseTempTypedValue(value);
1133         }
1134     }
1135 
1136     @NonNull
loadColorStateList(@onNull TypedValue value, int id, @Nullable Theme theme)1137     ColorStateList loadColorStateList(@NonNull TypedValue value, int id, @Nullable Theme theme)
1138             throws NotFoundException {
1139         return mResourcesImpl.loadColorStateList(this, value, id, theme);
1140     }
1141 
1142     /**
1143      * @hide
1144      */
1145     @NonNull
loadComplexColor(@onNull TypedValue value, int id, @Nullable Theme theme)1146     public ComplexColor loadComplexColor(@NonNull TypedValue value, int id, @Nullable Theme theme) {
1147         return mResourcesImpl.loadComplexColor(this, value, id, theme);
1148     }
1149 
1150     /**
1151      * Return a boolean associated with a particular resource ID.  This can be
1152      * used with any integral resource value, and will return true if it is
1153      * non-zero.
1154      *
1155      * @param id The desired resource identifier, as generated by the aapt
1156      *           tool. This integer encodes the package, type, and resource
1157      *           entry. The value 0 is an invalid identifier.
1158      *
1159      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1160      *
1161      * @return Returns the boolean value contained in the resource.
1162      */
getBoolean(@oolRes int id)1163     public boolean getBoolean(@BoolRes int id) throws NotFoundException {
1164         final TypedValue value = obtainTempTypedValue();
1165         try {
1166             mResourcesImpl.getValue(id, value, true);
1167             if (value.type >= TypedValue.TYPE_FIRST_INT
1168                     && value.type <= TypedValue.TYPE_LAST_INT) {
1169                 return value.data != 0;
1170             }
1171             throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
1172                     + " type #0x" + Integer.toHexString(value.type) + " is not valid");
1173         } finally {
1174             releaseTempTypedValue(value);
1175         }
1176     }
1177 
1178     /**
1179      * Return an integer associated with a particular resource ID.
1180      *
1181      * @param id The desired resource identifier, as generated by the aapt
1182      *           tool. This integer encodes the package, type, and resource
1183      *           entry. The value 0 is an invalid identifier.
1184      *
1185      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1186      *
1187      * @return Returns the integer value contained in the resource.
1188      */
getInteger(@ntegerRes int id)1189     public int getInteger(@IntegerRes int id) throws NotFoundException {
1190         final TypedValue value = obtainTempTypedValue();
1191         try {
1192             mResourcesImpl.getValue(id, value, true);
1193             if (value.type >= TypedValue.TYPE_FIRST_INT
1194                     && value.type <= TypedValue.TYPE_LAST_INT) {
1195                 return value.data;
1196             }
1197             throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
1198                     + " type #0x" + Integer.toHexString(value.type) + " is not valid");
1199         } finally {
1200             releaseTempTypedValue(value);
1201         }
1202     }
1203 
1204     /**
1205      * Retrieve a floating-point value for a particular resource ID.
1206      *
1207      * @param id The desired resource identifier, as generated by the aapt
1208      *           tool. This integer encodes the package, type, and resource
1209      *           entry. The value 0 is an invalid identifier.
1210      *
1211      * @return Returns the floating-point value contained in the resource.
1212      *
1213      * @throws NotFoundException Throws NotFoundException if the given ID does
1214      *         not exist or is not a floating-point value.
1215      */
getFloat(@imenRes int id)1216     public float getFloat(@DimenRes int id) {
1217         final TypedValue value = obtainTempTypedValue();
1218         try {
1219             mResourcesImpl.getValue(id, value, true);
1220             if (value.type == TypedValue.TYPE_FLOAT) {
1221                 return value.getFloat();
1222             }
1223             throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
1224                     + " type #0x" + Integer.toHexString(value.type) + " is not valid");
1225         } finally {
1226             releaseTempTypedValue(value);
1227         }
1228     }
1229 
1230     /**
1231      * Return an XmlResourceParser through which you can read a view layout
1232      * description for the given resource ID.  This parser has limited
1233      * functionality -- in particular, you can't change its input, and only
1234      * the high-level events are available.
1235      *
1236      * <p>This function is really a simple wrapper for calling
1237      * {@link #getXml} with a layout resource.
1238      *
1239      * @param id The desired resource identifier, as generated by the aapt
1240      *           tool. This integer encodes the package, type, and resource
1241      *           entry. The value 0 is an invalid identifier.
1242      *
1243      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1244      *
1245      * @return A new parser object through which you can read
1246      *         the XML data.
1247      *
1248      * @see #getXml
1249      */
1250     @NonNull
getLayout(@ayoutRes int id)1251     public XmlResourceParser getLayout(@LayoutRes int id) throws NotFoundException {
1252         return loadXmlResourceParser(id, "layout");
1253     }
1254 
1255     /**
1256      * Return an XmlResourceParser through which you can read an animation
1257      * description for the given resource ID.  This parser has limited
1258      * functionality -- in particular, you can't change its input, and only
1259      * the high-level events are available.
1260      *
1261      * <p>This function is really a simple wrapper for calling
1262      * {@link #getXml} with an animation resource.
1263      *
1264      * @param id The desired resource identifier, as generated by the aapt
1265      *           tool. This integer encodes the package, type, and resource
1266      *           entry. The value 0 is an invalid identifier.
1267      *
1268      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1269      *
1270      * @return A new parser object through which you can read
1271      *         the XML data.
1272      *
1273      * @see #getXml
1274      */
1275     @NonNull
getAnimation(@nimatorRes @nimRes int id)1276     public XmlResourceParser getAnimation(@AnimatorRes @AnimRes int id) throws NotFoundException {
1277         return loadXmlResourceParser(id, "anim");
1278     }
1279 
1280     /**
1281      * Return an XmlResourceParser through which you can read a generic XML
1282      * resource for the given resource ID.
1283      *
1284      * <p>The XmlPullParser implementation returned here has some limited
1285      * functionality.  In particular, you can't change its input, and only
1286      * high-level parsing events are available (since the document was
1287      * pre-parsed for you at build time, which involved merging text and
1288      * stripping comments).
1289      *
1290      * @param id The desired resource identifier, as generated by the aapt
1291      *           tool. This integer encodes the package, type, and resource
1292      *           entry. The value 0 is an invalid identifier.
1293      *
1294      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1295      *
1296      * @return A new parser object through which you can read
1297      *         the XML data.
1298      *
1299      * @see android.util.AttributeSet
1300      */
1301     @NonNull
getXml(@mlRes int id)1302     public XmlResourceParser getXml(@XmlRes int id) throws NotFoundException {
1303         return loadXmlResourceParser(id, "xml");
1304     }
1305 
1306     /**
1307      * Open a data stream for reading a raw resource.  This can only be used
1308      * with resources whose value is the name of an asset files -- that is, it can be
1309      * used to open drawable, sound, and raw resources; it will fail on string
1310      * and color resources.
1311      *
1312      * @param id The resource identifier to open, as generated by the aapt tool.
1313      *
1314      * @return InputStream Access to the resource data.
1315      *
1316      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1317      */
1318     @NonNull
openRawResource(@awRes int id)1319     public InputStream openRawResource(@RawRes int id) throws NotFoundException {
1320         final TypedValue value = obtainTempTypedValue();
1321         try {
1322             return openRawResource(id, value);
1323         } finally {
1324             releaseTempTypedValue(value);
1325         }
1326     }
1327 
1328     /**
1329      * Returns a TypedValue suitable for temporary use. The obtained TypedValue
1330      * should be released using {@link #releaseTempTypedValue(TypedValue)}.
1331      *
1332      * @return a typed value suitable for temporary use
1333      */
obtainTempTypedValue()1334     private TypedValue obtainTempTypedValue() {
1335         TypedValue tmpValue = null;
1336         synchronized (mTmpValueLock) {
1337             if (mTmpValue != null) {
1338                 tmpValue = mTmpValue;
1339                 mTmpValue = null;
1340             }
1341         }
1342         if (tmpValue == null) {
1343             return new TypedValue();
1344         }
1345         return tmpValue;
1346     }
1347 
1348     /**
1349      * Returns a TypedValue to the pool. After calling this method, the
1350      * specified TypedValue should no longer be accessed.
1351      *
1352      * @param value the typed value to return to the pool
1353      */
releaseTempTypedValue(TypedValue value)1354     private void releaseTempTypedValue(TypedValue value) {
1355         synchronized (mTmpValueLock) {
1356             if (mTmpValue == null) {
1357                 mTmpValue = value;
1358             }
1359         }
1360     }
1361 
1362     /**
1363      * Open a data stream for reading a raw resource.  This can only be used
1364      * with resources whose value is the name of an asset file -- that is, it can be
1365      * used to open drawable, sound, and raw resources; it will fail on string
1366      * and color resources.
1367      *
1368      * @param id The resource identifier to open, as generated by the aapt tool.
1369      * @param value The TypedValue object to hold the resource information.
1370      *
1371      * @return InputStream Access to the resource data.
1372      *
1373      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1374      */
1375     @NonNull
openRawResource(@awRes int id, TypedValue value)1376     public InputStream openRawResource(@RawRes int id, TypedValue value)
1377             throws NotFoundException {
1378         return mResourcesImpl.openRawResource(id, value);
1379     }
1380 
1381     /**
1382      * Open a file descriptor for reading a raw resource.  This can only be used
1383      * with resources whose value is the name of an asset files -- that is, it can be
1384      * used to open drawable, sound, and raw resources; it will fail on string
1385      * and color resources.
1386      *
1387      * <p>This function only works for resources that are stored in the package
1388      * as uncompressed data, which typically includes things like mp3 files
1389      * and png images.
1390      *
1391      * @param id The resource identifier to open, as generated by the aapt tool.
1392      *
1393      * @return AssetFileDescriptor A new file descriptor you can use to read
1394      * the resource.  This includes the file descriptor itself, as well as the
1395      * offset and length of data where the resource appears in the file.  A
1396      * null is returned if the file exists but is compressed.
1397      *
1398      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1399      *
1400      */
openRawResourceFd(@awRes int id)1401     public AssetFileDescriptor openRawResourceFd(@RawRes int id)
1402             throws NotFoundException {
1403         final TypedValue value = obtainTempTypedValue();
1404         try {
1405             return mResourcesImpl.openRawResourceFd(id, value);
1406         } finally {
1407             releaseTempTypedValue(value);
1408         }
1409     }
1410 
1411     /**
1412      * Return the raw data associated with a particular resource ID.
1413      *
1414      * @param id The desired resource identifier, as generated by the aapt
1415      *           tool. This integer encodes the package, type, and resource
1416      *           entry. The value 0 is an invalid identifier.
1417      * @param outValue Object in which to place the resource data.
1418      * @param resolveRefs If true, a resource that is a reference to another
1419      *                    resource will be followed so that you receive the
1420      *                    actual final resource data.  If false, the TypedValue
1421      *                    will be filled in with the reference itself.
1422      *
1423      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1424      *
1425      */
getValue(@nyRes int id, TypedValue outValue, boolean resolveRefs)1426     public void getValue(@AnyRes int id, TypedValue outValue, boolean resolveRefs)
1427             throws NotFoundException {
1428         mResourcesImpl.getValue(id, outValue, resolveRefs);
1429     }
1430 
1431     /**
1432      * Get the raw value associated with a resource with associated density.
1433      *
1434      * @param id resource identifier
1435      * @param density density in DPI
1436      * @param resolveRefs If true, a resource that is a reference to another
1437      *            resource will be followed so that you receive the actual final
1438      *            resource data. If false, the TypedValue will be filled in with
1439      *            the reference itself.
1440      * @throws NotFoundException Throws NotFoundException if the given ID does
1441      *             not exist.
1442      * @see #getValue(String, TypedValue, boolean)
1443      */
getValueForDensity(@nyRes int id, int density, TypedValue outValue, boolean resolveRefs)1444     public void getValueForDensity(@AnyRes int id, int density, TypedValue outValue,
1445             boolean resolveRefs) throws NotFoundException {
1446         mResourcesImpl.getValueForDensity(id, density, outValue, resolveRefs);
1447     }
1448 
1449     /**
1450      * Return the raw data associated with a particular resource ID.
1451      * See getIdentifier() for information on how names are mapped to resource
1452      * IDs, and getString(int) for information on how string resources are
1453      * retrieved.
1454      *
1455      * <p>Note: use of this function is discouraged.  It is much more
1456      * efficient to retrieve resources by identifier than by name.
1457      *
1458      * @param name The name of the desired resource.  This is passed to
1459      *             getIdentifier() with a default type of "string".
1460      * @param outValue Object in which to place the resource data.
1461      * @param resolveRefs If true, a resource that is a reference to another
1462      *                    resource will be followed so that you receive the
1463      *                    actual final resource data.  If false, the TypedValue
1464      *                    will be filled in with the reference itself.
1465      *
1466      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1467      *
1468      */
getValue(String name, TypedValue outValue, boolean resolveRefs)1469     public void getValue(String name, TypedValue outValue, boolean resolveRefs)
1470             throws NotFoundException {
1471         mResourcesImpl.getValue(name, outValue, resolveRefs);
1472     }
1473 
1474 
1475     /**
1476      * Returns the resource ID of the resource that was used to create this AttributeSet.
1477      *
1478      * @param set AttributeSet for which we want to find the source.
1479      * @return The resource ID for the source that is backing the given AttributeSet or
1480      * {@link Resources#ID_NULL} if the AttributeSet is {@code null}.
1481      */
1482     @AnyRes
getAttributeSetSourceResId(@ullable AttributeSet set)1483     public static int getAttributeSetSourceResId(@Nullable AttributeSet set) {
1484         return ResourcesImpl.getAttributeSetSourceResId(set);
1485     }
1486 
1487     /**
1488      * This class holds the current attribute values for a particular theme.
1489      * In other words, a Theme is a set of values for resource attributes;
1490      * these are used in conjunction with {@link TypedArray}
1491      * to resolve the final value for an attribute.
1492      *
1493      * <p>The Theme's attributes come into play in two ways: (1) a styled
1494      * attribute can explicit reference a value in the theme through the
1495      * "?themeAttribute" syntax; (2) if no value has been defined for a
1496      * particular styled attribute, as a last resort we will try to find that
1497      * attribute's value in the Theme.
1498      *
1499      * <p>You will normally use the {@link #obtainStyledAttributes} APIs to
1500      * retrieve XML attributes with style and theme information applied.
1501      */
1502     public final class Theme {
1503         @UnsupportedAppUsage
1504         private ResourcesImpl.ThemeImpl mThemeImpl;
1505 
Theme()1506         private Theme() {
1507         }
1508 
setImpl(ResourcesImpl.ThemeImpl impl)1509         void setImpl(ResourcesImpl.ThemeImpl impl) {
1510             mThemeImpl = impl;
1511         }
1512 
1513         /**
1514          * Place new attribute values into the theme.  The style resource
1515          * specified by <var>resid</var> will be retrieved from this Theme's
1516          * resources, its values placed into the Theme object.
1517          *
1518          * <p>The semantics of this function depends on the <var>force</var>
1519          * argument:  If false, only values that are not already defined in
1520          * the theme will be copied from the system resource; otherwise, if
1521          * any of the style's attributes are already defined in the theme, the
1522          * current values in the theme will be overwritten.
1523          *
1524          * @param resId The resource ID of a style resource from which to
1525          *              obtain attribute values.
1526          * @param force If true, values in the style resource will always be
1527          *              used in the theme; otherwise, they will only be used
1528          *              if not already defined in the theme.
1529          */
applyStyle(int resId, boolean force)1530         public void applyStyle(int resId, boolean force) {
1531             mThemeImpl.applyStyle(resId, force);
1532         }
1533 
1534         /**
1535          * Set this theme to hold the same contents as the theme
1536          * <var>other</var>.  If both of these themes are from the same
1537          * Resources object, they will be identical after this function
1538          * returns.  If they are from different Resources, only the resources
1539          * they have in common will be set in this theme.
1540          *
1541          * @param other The existing Theme to copy from.
1542          */
setTo(Theme other)1543         public void setTo(Theme other) {
1544             mThemeImpl.setTo(other.mThemeImpl);
1545         }
1546 
1547         /**
1548          * Return a TypedArray holding the values defined by
1549          * <var>Theme</var> which are listed in <var>attrs</var>.
1550          *
1551          * <p>Be sure to call {@link TypedArray#recycle() TypedArray.recycle()} when you are done
1552          * with the array.
1553          *
1554          * @param attrs The desired attributes. These attribute IDs must be sorted in ascending
1555          *              order.
1556          *
1557          * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1558          *
1559          * @return Returns a TypedArray holding an array of the attribute values.
1560          * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
1561          * when done with it.
1562          *
1563          * @see Resources#obtainAttributes
1564          * @see #obtainStyledAttributes(int, int[])
1565          * @see #obtainStyledAttributes(AttributeSet, int[], int, int)
1566          */
1567         @NonNull
obtainStyledAttributes(@onNull @tyleableRes int[] attrs)1568         public TypedArray obtainStyledAttributes(@NonNull @StyleableRes int[] attrs) {
1569             return mThemeImpl.obtainStyledAttributes(this, null, attrs, 0, 0);
1570         }
1571 
1572         /**
1573          * Return a TypedArray holding the values defined by the style
1574          * resource <var>resid</var> which are listed in <var>attrs</var>.
1575          *
1576          * <p>Be sure to call {@link TypedArray#recycle() TypedArray.recycle()} when you are done
1577          * with the array.
1578          *
1579          * @param resId The desired style resource.
1580          * @param attrs The desired attributes in the style. These attribute IDs must be sorted in
1581          *              ascending order.
1582          *
1583          * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
1584          *
1585          * @return Returns a TypedArray holding an array of the attribute values.
1586          * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
1587          * when done with it.
1588          *
1589          * @see Resources#obtainAttributes
1590          * @see #obtainStyledAttributes(int[])
1591          * @see #obtainStyledAttributes(AttributeSet, int[], int, int)
1592          */
1593         @NonNull
obtainStyledAttributes(@tyleRes int resId, @NonNull @StyleableRes int[] attrs)1594         public TypedArray obtainStyledAttributes(@StyleRes int resId,
1595                 @NonNull @StyleableRes int[] attrs)
1596                 throws NotFoundException {
1597             return mThemeImpl.obtainStyledAttributes(this, null, attrs, 0, resId);
1598         }
1599 
1600         /**
1601          * Return a TypedArray holding the attribute values in
1602          * <var>set</var>
1603          * that are listed in <var>attrs</var>.  In addition, if the given
1604          * AttributeSet specifies a style class (through the "style" attribute),
1605          * that style will be applied on top of the base attributes it defines.
1606          *
1607          * <p>Be sure to call {@link TypedArray#recycle() TypedArray.recycle()} when you are done
1608          * with the array.
1609          *
1610          * <p>When determining the final value of a particular attribute, there
1611          * are four inputs that come into play:</p>
1612          *
1613          * <ol>
1614          *     <li> Any attribute values in the given AttributeSet.
1615          *     <li> The style resource specified in the AttributeSet (named
1616          *     "style").
1617          *     <li> The default style specified by <var>defStyleAttr</var> and
1618          *     <var>defStyleRes</var>
1619          *     <li> The base values in this theme.
1620          * </ol>
1621          *
1622          * <p>Each of these inputs is considered in-order, with the first listed
1623          * taking precedence over the following ones.  In other words, if in the
1624          * AttributeSet you have supplied <code>&lt;Button
1625          * textColor="#ff000000"&gt;</code>, then the button's text will
1626          * <em>always</em> be black, regardless of what is specified in any of
1627          * the styles.
1628          *
1629          * @param set The base set of attribute values.  May be null.
1630          * @param attrs The desired attributes to be retrieved. These attribute IDs must be sorted
1631          *              in ascending order.
1632          * @param defStyleAttr An attribute in the current theme that contains a
1633          *                     reference to a style resource that supplies
1634          *                     defaults values for the TypedArray.  Can be
1635          *                     0 to not look for defaults.
1636          * @param defStyleRes A resource identifier of a style resource that
1637          *                    supplies default values for the TypedArray,
1638          *                    used only if defStyleAttr is 0 or can not be found
1639          *                    in the theme.  Can be 0 to not look for defaults.
1640          *
1641          * @return Returns a TypedArray holding an array of the attribute values.
1642          * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
1643          * when done with it.
1644          *
1645          * @see Resources#obtainAttributes
1646          * @see #obtainStyledAttributes(int[])
1647          * @see #obtainStyledAttributes(int, int[])
1648          */
1649         @NonNull
obtainStyledAttributes(@ullable AttributeSet set, @NonNull @StyleableRes int[] attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes)1650         public TypedArray obtainStyledAttributes(@Nullable AttributeSet set,
1651                 @NonNull @StyleableRes int[] attrs, @AttrRes int defStyleAttr,
1652                 @StyleRes int defStyleRes) {
1653             return mThemeImpl.obtainStyledAttributes(this, set, attrs, defStyleAttr, defStyleRes);
1654         }
1655 
1656         /**
1657          * Retrieve the values for a set of attributes in the Theme. The
1658          * contents of the typed array are ultimately filled in by
1659          * {@link Resources#getValue}.
1660          *
1661          * @param values The base set of attribute values, must be equal in
1662          *               length to {@code attrs}. All values must be of type
1663          *               {@link TypedValue#TYPE_ATTRIBUTE}.
1664          * @param attrs The desired attributes to be retrieved. These attribute IDs must be sorted
1665          *              in ascending order.
1666          * @return Returns a TypedArray holding an array of the attribute
1667          *         values. Be sure to call {@link TypedArray#recycle()}
1668          *         when done with it.
1669          * @hide
1670          */
1671         @NonNull
1672         @UnsupportedAppUsage
resolveAttributes(@onNull int[] values, @NonNull int[] attrs)1673         public TypedArray resolveAttributes(@NonNull int[] values, @NonNull int[] attrs) {
1674             return mThemeImpl.resolveAttributes(this, values, attrs);
1675         }
1676 
1677         /**
1678          * Retrieve the value of an attribute in the Theme.  The contents of
1679          * <var>outValue</var> are ultimately filled in by
1680          * {@link Resources#getValue}.
1681          *
1682          * @param resid The resource identifier of the desired theme
1683          *              attribute.
1684          * @param outValue Filled in with the ultimate resource value supplied
1685          *                 by the attribute.
1686          * @param resolveRefs If true, resource references will be walked; if
1687          *                    false, <var>outValue</var> may be a
1688          *                    TYPE_REFERENCE.  In either case, it will never
1689          *                    be a TYPE_ATTRIBUTE.
1690          *
1691          * @return boolean Returns true if the attribute was found and
1692          *         <var>outValue</var> is valid, else false.
1693          */
resolveAttribute(int resid, TypedValue outValue, boolean resolveRefs)1694         public boolean resolveAttribute(int resid, TypedValue outValue, boolean resolveRefs) {
1695             return mThemeImpl.resolveAttribute(resid, outValue, resolveRefs);
1696         }
1697 
1698         /**
1699          * Gets all of the attribute ids associated with this {@link Theme}. For debugging only.
1700          *
1701          * @return The int array containing attribute ids associated with this {@link Theme}.
1702          * @hide
1703          */
getAllAttributes()1704         public int[] getAllAttributes() {
1705             return mThemeImpl.getAllAttributes();
1706         }
1707 
1708         /**
1709          * Returns the resources to which this theme belongs.
1710          *
1711          * @return Resources to which this theme belongs.
1712          */
getResources()1713         public Resources getResources() {
1714             return Resources.this;
1715         }
1716 
1717         /**
1718          * Return a drawable object associated with a particular resource ID
1719          * and styled for the Theme.
1720          *
1721          * @param id The desired resource identifier, as generated by the aapt
1722          *           tool. This integer encodes the package, type, and resource
1723          *           entry. The value 0 is an invalid identifier.
1724          * @return Drawable An object that can be used to draw this resource.
1725          * @throws NotFoundException Throws NotFoundException if the given ID
1726          *         does not exist.
1727          */
getDrawable(@rawableRes int id)1728         public Drawable getDrawable(@DrawableRes int id) throws NotFoundException {
1729             return Resources.this.getDrawable(id, this);
1730         }
1731 
1732         /**
1733          * Returns a bit mask of configuration changes that will impact this
1734          * theme (and thus require completely reloading it).
1735          *
1736          * @return a bit mask of configuration changes, as defined by
1737          *         {@link ActivityInfo}
1738          * @see ActivityInfo
1739          */
getChangingConfigurations()1740         public @Config int getChangingConfigurations() {
1741             return mThemeImpl.getChangingConfigurations();
1742         }
1743 
1744         /**
1745          * Print contents of this theme out to the log.  For debugging only.
1746          *
1747          * @param priority The log priority to use.
1748          * @param tag The log tag to use.
1749          * @param prefix Text to prefix each line printed.
1750          */
dump(int priority, String tag, String prefix)1751         public void dump(int priority, String tag, String prefix) {
1752             mThemeImpl.dump(priority, tag, prefix);
1753         }
1754 
1755         // Needed by layoutlib.
getNativeTheme()1756         /*package*/ long getNativeTheme() {
1757             return mThemeImpl.getNativeTheme();
1758         }
1759 
getAppliedStyleResId()1760         /*package*/ int getAppliedStyleResId() {
1761             return mThemeImpl.getAppliedStyleResId();
1762         }
1763 
1764         /**
1765          * @hide
1766          */
getKey()1767         public ThemeKey getKey() {
1768             return mThemeImpl.getKey();
1769         }
1770 
getResourceNameFromHexString(String hexString)1771         private String getResourceNameFromHexString(String hexString) {
1772             return getResourceName(Integer.parseInt(hexString, 16));
1773         }
1774 
1775         /**
1776          * Parses {@link #getKey()} and returns a String array that holds pairs of
1777          * adjacent Theme data: resource name followed by whether or not it was
1778          * forced, as specified by {@link #applyStyle(int, boolean)}.
1779          *
1780          * @hide
1781          */
1782         @ViewDebug.ExportedProperty(category = "theme", hasAdjacentMapping = true)
getTheme()1783         public String[] getTheme() {
1784             return mThemeImpl.getTheme();
1785         }
1786 
1787         /** @hide */
encode(@onNull ViewHierarchyEncoder encoder)1788         public void encode(@NonNull ViewHierarchyEncoder encoder) {
1789             encoder.beginObject(this);
1790             final String[] properties = getTheme();
1791             for (int i = 0; i < properties.length; i += 2) {
1792                 encoder.addProperty(properties[i], properties[i+1]);
1793             }
1794             encoder.endObject();
1795         }
1796 
1797         /**
1798          * Rebases the theme against the parent Resource object's current
1799          * configuration by re-applying the styles passed to
1800          * {@link #applyStyle(int, boolean)}.
1801          */
rebase()1802         public void rebase() {
1803             mThemeImpl.rebase();
1804         }
1805 
1806         /**
1807          * Returns the resource ID for the style specified using {@code style="..."} in the
1808          * {@link AttributeSet}'s backing XML element or {@link Resources#ID_NULL} otherwise if not
1809          * specified or otherwise not applicable.
1810          * <p>
1811          * Each {@link android.view.View} can have an explicit style specified in the layout file.
1812          * This style is used first during the {@link android.view.View} attribute resolution, then
1813          * if an attribute is not defined there the resource system looks at default style and theme
1814          * as fallbacks.
1815          *
1816          * @param set The base set of attribute values.
1817          *
1818          * @return The resource ID for the style specified using {@code style="..."} in the
1819          *      {@link AttributeSet}'s backing XML element or {@link Resources#ID_NULL} otherwise
1820          *      if not specified or otherwise not applicable.
1821          */
1822         @StyleRes
getExplicitStyle(@ullable AttributeSet set)1823         public int getExplicitStyle(@Nullable AttributeSet set) {
1824             if (set == null) {
1825                 return ID_NULL;
1826             }
1827             int styleAttr = set.getStyleAttribute();
1828             if (styleAttr == ID_NULL) {
1829                 return ID_NULL;
1830             }
1831             String styleAttrType = getResources().getResourceTypeName(styleAttr);
1832             if ("attr".equals(styleAttrType)) {
1833                 TypedValue explicitStyle = new TypedValue();
1834                 boolean resolved = resolveAttribute(styleAttr, explicitStyle, true);
1835                 if (resolved) {
1836                     return explicitStyle.resourceId;
1837                 }
1838             } else if ("style".equals(styleAttrType)) {
1839                 return styleAttr;
1840             }
1841             return ID_NULL;
1842         }
1843 
1844         /**
1845          * Returns the ordered list of resource ID that are considered when resolving attribute
1846          * values when making an equivalent call to
1847          * {@link #obtainStyledAttributes(AttributeSet, int[], int, int)} . The list will include
1848          * a set of explicit styles ({@code explicitStyleRes} and it will include the default styles
1849          * ({@code defStyleAttr} and {@code defStyleRes}).
1850          *
1851          * @param defStyleAttr An attribute in the current theme that contains a
1852          *                     reference to a style resource that supplies
1853          *                     defaults values for the TypedArray.  Can be
1854          *                     0 to not look for defaults.
1855          * @param defStyleRes A resource identifier of a style resource that
1856          *                    supplies default values for the TypedArray,
1857          *                    used only if defStyleAttr is 0 or can not be found
1858          *                    in the theme.  Can be 0 to not look for defaults.
1859          * @param explicitStyleRes A resource identifier of an explicit style resource.
1860          * @return ordered list of resource ID that are considered when resolving attribute values.
1861          */
1862         @NonNull
getAttributeResolutionStack(@ttrRes int defStyleAttr, @StyleRes int defStyleRes, @StyleRes int explicitStyleRes)1863         public int[] getAttributeResolutionStack(@AttrRes int defStyleAttr,
1864                 @StyleRes int defStyleRes, @StyleRes int explicitStyleRes) {
1865             int[] stack = mThemeImpl.getAttributeResolutionStack(
1866                     defStyleAttr, defStyleRes, explicitStyleRes);
1867             if (stack == null) {
1868                 return new int[0];
1869             } else {
1870                 return stack;
1871             }
1872         }
1873     }
1874 
1875     static class ThemeKey implements Cloneable {
1876         int[] mResId;
1877         boolean[] mForce;
1878         int mCount;
1879 
1880         private int mHashCode = 0;
1881 
append(int resId, boolean force)1882         public void append(int resId, boolean force) {
1883             if (mResId == null) {
1884                 mResId = new int[4];
1885             }
1886 
1887             if (mForce == null) {
1888                 mForce = new boolean[4];
1889             }
1890 
1891             mResId = GrowingArrayUtils.append(mResId, mCount, resId);
1892             mForce = GrowingArrayUtils.append(mForce, mCount, force);
1893             mCount++;
1894 
1895             mHashCode = 31 * (31 * mHashCode + resId) + (force ? 1 : 0);
1896         }
1897 
1898         /**
1899          * Sets up this key as a deep copy of another key.
1900          *
1901          * @param other the key to deep copy into this key
1902          */
setTo(ThemeKey other)1903         public void setTo(ThemeKey other) {
1904             mResId = other.mResId == null ? null : other.mResId.clone();
1905             mForce = other.mForce == null ? null : other.mForce.clone();
1906             mCount = other.mCount;
1907             mHashCode = other.mHashCode;
1908         }
1909 
1910         @Override
hashCode()1911         public int hashCode() {
1912             return mHashCode;
1913         }
1914 
1915         @Override
equals(Object o)1916         public boolean equals(Object o) {
1917             if (this == o) {
1918                 return true;
1919             }
1920 
1921             if (o == null || getClass() != o.getClass() || hashCode() != o.hashCode()) {
1922                 return false;
1923             }
1924 
1925             final ThemeKey t = (ThemeKey) o;
1926             if (mCount != t.mCount) {
1927                 return false;
1928             }
1929 
1930             final int N = mCount;
1931             for (int i = 0; i < N; i++) {
1932                 if (mResId[i] != t.mResId[i] || mForce[i] != t.mForce[i]) {
1933                     return false;
1934                 }
1935             }
1936 
1937             return true;
1938         }
1939 
1940         /**
1941          * @return a shallow copy of this key
1942          */
1943         @Override
clone()1944         public ThemeKey clone() {
1945             final ThemeKey other = new ThemeKey();
1946             other.mResId = mResId;
1947             other.mForce = mForce;
1948             other.mCount = mCount;
1949             other.mHashCode = mHashCode;
1950             return other;
1951         }
1952     }
1953 
1954     /**
1955      * Generate a new Theme object for this set of Resources.  It initially
1956      * starts out empty.
1957      *
1958      * @return Theme The newly created Theme container.
1959      */
newTheme()1960     public final Theme newTheme() {
1961         Theme theme = new Theme();
1962         theme.setImpl(mResourcesImpl.newThemeImpl());
1963         synchronized (mThemeRefs) {
1964             mThemeRefs.add(new WeakReference<>(theme));
1965 
1966             // Clean up references to garbage collected themes
1967             if (mThemeRefs.size() > mThemeRefsNextFlushSize) {
1968                 mThemeRefs.removeIf(ref -> ref.get() == null);
1969                 mThemeRefsNextFlushSize = Math.max(MIN_THEME_REFS_FLUSH_SIZE,
1970                         2 * mThemeRefs.size());
1971             }
1972         }
1973         return theme;
1974     }
1975 
1976     /**
1977      * Retrieve a set of basic attribute values from an AttributeSet, not
1978      * performing styling of them using a theme and/or style resources.
1979      *
1980      * @param set The current attribute values to retrieve.
1981      * @param attrs The specific attributes to be retrieved. These attribute IDs must be sorted in
1982      *              ascending order.
1983      * @return Returns a TypedArray holding an array of the attribute values.
1984      * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
1985      * when done with it.
1986      *
1987      * @see Theme#obtainStyledAttributes(AttributeSet, int[], int, int)
1988      */
obtainAttributes(AttributeSet set, @StyleableRes int[] attrs)1989     public TypedArray obtainAttributes(AttributeSet set, @StyleableRes int[] attrs) {
1990         int len = attrs.length;
1991         TypedArray array = TypedArray.obtain(this, len);
1992 
1993         // XXX note that for now we only work with compiled XML files.
1994         // To support generic XML files we will need to manually parse
1995         // out the attributes from the XML file (applying type information
1996         // contained in the resources and such).
1997         XmlBlock.Parser parser = (XmlBlock.Parser)set;
1998         mResourcesImpl.getAssets().retrieveAttributes(parser, attrs, array.mData, array.mIndices);
1999 
2000         array.mXml = parser;
2001 
2002         return array;
2003     }
2004 
2005     /**
2006      * Store the newly updated configuration.
2007      *
2008      * @deprecated See {@link android.content.Context#createConfigurationContext(Configuration)}.
2009      */
2010     @Deprecated
updateConfiguration(Configuration config, DisplayMetrics metrics)2011     public void updateConfiguration(Configuration config, DisplayMetrics metrics) {
2012         updateConfiguration(config, metrics, null);
2013     }
2014 
2015     /**
2016      * @hide
2017      */
updateConfiguration(Configuration config, DisplayMetrics metrics, CompatibilityInfo compat)2018     public void updateConfiguration(Configuration config, DisplayMetrics metrics,
2019                                     CompatibilityInfo compat) {
2020         mResourcesImpl.updateConfiguration(config, metrics, compat);
2021     }
2022 
2023     /**
2024      * Update the system resources configuration if they have previously
2025      * been initialized.
2026      *
2027      * @hide
2028      */
2029     @UnsupportedAppUsage
updateSystemConfiguration(Configuration config, DisplayMetrics metrics, CompatibilityInfo compat)2030     public static void updateSystemConfiguration(Configuration config, DisplayMetrics metrics,
2031             CompatibilityInfo compat) {
2032         if (mSystem != null) {
2033             mSystem.updateConfiguration(config, metrics, compat);
2034             //Log.i(TAG, "Updated system resources " + mSystem
2035             //        + ": " + mSystem.getConfiguration());
2036         }
2037     }
2038 
2039     /**
2040      * Return the current display metrics that are in effect for this resource
2041      * object. The returned object should be treated as read-only.
2042      *
2043      * <p>Note that the reported value may be different than the window this application is
2044      * interested in.</p>
2045      *
2046      * <p>Best practices are to obtain metrics from {@link WindowManager#getCurrentWindowMetrics()}
2047      * for window bounds, {@link Display#getRealMetrics(DisplayMetrics)} for display bounds and
2048      * obtain density from {@link Configuration#densityDpi}. The value obtained from this API may be
2049      * wrong if the {@link Resources} is from the context which is different than the window is
2050      * attached such as {@link Application#getResources()}.
2051      * <p/>
2052      *
2053      * @return The resource's current display metrics.
2054      */
getDisplayMetrics()2055     public DisplayMetrics getDisplayMetrics() {
2056         return mResourcesImpl.getDisplayMetrics();
2057     }
2058 
2059     /** @hide */
2060     @UnsupportedAppUsage
getDisplayAdjustments()2061     public DisplayAdjustments getDisplayAdjustments() {
2062         final DisplayAdjustments overrideDisplayAdjustments = mOverrideDisplayAdjustments;
2063         if (overrideDisplayAdjustments != null) {
2064             return overrideDisplayAdjustments;
2065         }
2066         return mResourcesImpl.getDisplayAdjustments();
2067     }
2068 
2069     /**
2070      * Customize the display adjustments based on the current one in {@link #mResourcesImpl}, in
2071      * order to isolate the effect with other instances of {@link Resource} that may share the same
2072      * instance of {@link ResourcesImpl}.
2073      *
2074      * @param override The operation to override the existing display adjustments. If it is null,
2075      *                 the override adjustments will be cleared.
2076      * @hide
2077      */
overrideDisplayAdjustments(@ullable Consumer<DisplayAdjustments> override)2078     public void overrideDisplayAdjustments(@Nullable Consumer<DisplayAdjustments> override) {
2079         if (override != null) {
2080             mOverrideDisplayAdjustments = new DisplayAdjustments(
2081                     mResourcesImpl.getDisplayAdjustments());
2082             override.accept(mOverrideDisplayAdjustments);
2083         } else {
2084             mOverrideDisplayAdjustments = null;
2085         }
2086     }
2087 
2088     /**
2089      * Return {@code true} if the override display adjustments have been set.
2090      * @hide
2091      */
hasOverrideDisplayAdjustments()2092     public boolean hasOverrideDisplayAdjustments() {
2093         return mOverrideDisplayAdjustments != null;
2094     }
2095 
2096     /**
2097      * Return the current configuration that is in effect for this resource
2098      * object.  The returned object should be treated as read-only.
2099      *
2100      * @return The resource's current configuration.
2101      */
getConfiguration()2102     public Configuration getConfiguration() {
2103         return mResourcesImpl.getConfiguration();
2104     }
2105 
2106     /** @hide */
getSizeConfigurations()2107     public Configuration[] getSizeConfigurations() {
2108         return mResourcesImpl.getSizeConfigurations();
2109     }
2110 
2111     /**
2112      * Return the compatibility mode information for the application.
2113      * The returned object should be treated as read-only.
2114      *
2115      * @return compatibility info.
2116      * @hide
2117      */
2118     @UnsupportedAppUsage
getCompatibilityInfo()2119     public CompatibilityInfo getCompatibilityInfo() {
2120         return mResourcesImpl.getCompatibilityInfo();
2121     }
2122 
2123     /**
2124      * This is just for testing.
2125      * @hide
2126      */
2127     @VisibleForTesting
2128     @UnsupportedAppUsage
setCompatibilityInfo(CompatibilityInfo ci)2129     public void setCompatibilityInfo(CompatibilityInfo ci) {
2130         if (ci != null) {
2131             mResourcesImpl.updateConfiguration(null, null, ci);
2132         }
2133     }
2134 
2135     /**
2136      * Return a resource identifier for the given resource name.  A fully
2137      * qualified resource name is of the form "package:type/entry".  The first
2138      * two components (package and type) are optional if defType and
2139      * defPackage, respectively, are specified here.
2140      *
2141      * <p>Note: use of this function is discouraged.  It is much more
2142      * efficient to retrieve resources by identifier than by name.
2143      *
2144      * @param name The name of the desired resource.
2145      * @param defType Optional default resource type to find, if "type/" is
2146      *                not included in the name.  Can be null to require an
2147      *                explicit type.
2148      * @param defPackage Optional default package to find, if "package:" is
2149      *                   not included in the name.  Can be null to require an
2150      *                   explicit package.
2151      *
2152      * @return int The associated resource identifier.  Returns 0 if no such
2153      *         resource was found.  (0 is not a valid resource ID.)
2154      */
getIdentifier(String name, String defType, String defPackage)2155     public int getIdentifier(String name, String defType, String defPackage) {
2156         return mResourcesImpl.getIdentifier(name, defType, defPackage);
2157     }
2158 
2159     /**
2160      * Return true if given resource identifier includes a package.
2161      *
2162      * @hide
2163      */
resourceHasPackage(@nyRes int resid)2164     public static boolean resourceHasPackage(@AnyRes int resid) {
2165         return (resid >>> 24) != 0;
2166     }
2167 
2168     /**
2169      * Return the full name for a given resource identifier.  This name is
2170      * a single string of the form "package:type/entry".
2171      *
2172      * @param resid The resource identifier whose name is to be retrieved.
2173      *
2174      * @return A string holding the name of the resource.
2175      *
2176      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
2177      *
2178      * @see #getResourcePackageName
2179      * @see #getResourceTypeName
2180      * @see #getResourceEntryName
2181      */
getResourceName(@nyRes int resid)2182     public String getResourceName(@AnyRes int resid) throws NotFoundException {
2183         return mResourcesImpl.getResourceName(resid);
2184     }
2185 
2186     /**
2187      * Return the package name for a given resource identifier.
2188      *
2189      * @param resid The resource identifier whose package name is to be
2190      * retrieved.
2191      *
2192      * @return A string holding the package name of the resource.
2193      *
2194      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
2195      *
2196      * @see #getResourceName
2197      */
getResourcePackageName(@nyRes int resid)2198     public String getResourcePackageName(@AnyRes int resid) throws NotFoundException {
2199         return mResourcesImpl.getResourcePackageName(resid);
2200     }
2201 
2202     /**
2203      * Return the type name for a given resource identifier.
2204      *
2205      * @param resid The resource identifier whose type name is to be
2206      * retrieved.
2207      *
2208      * @return A string holding the type name of the resource.
2209      *
2210      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
2211      *
2212      * @see #getResourceName
2213      */
getResourceTypeName(@nyRes int resid)2214     public String getResourceTypeName(@AnyRes int resid) throws NotFoundException {
2215         return mResourcesImpl.getResourceTypeName(resid);
2216     }
2217 
2218     /**
2219      * Return the entry name for a given resource identifier.
2220      *
2221      * @param resid The resource identifier whose entry name is to be
2222      * retrieved.
2223      *
2224      * @return A string holding the entry name of the resource.
2225      *
2226      * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
2227      *
2228      * @see #getResourceName
2229      */
getResourceEntryName(@nyRes int resid)2230     public String getResourceEntryName(@AnyRes int resid) throws NotFoundException {
2231         return mResourcesImpl.getResourceEntryName(resid);
2232     }
2233 
2234     /**
2235      * Return formatted log of the last retrieved resource's resolution path.
2236      *
2237      * @return A string holding a formatted log of the steps taken to resolve the last resource.
2238      *
2239      * @throws NotFoundException Throws NotFoundException if there hasn't been a resource
2240      * resolved yet.
2241      *
2242      * @hide
2243      */
getLastResourceResolution()2244     public String getLastResourceResolution() throws NotFoundException {
2245         return mResourcesImpl.getLastResourceResolution();
2246     }
2247 
2248     /**
2249      * Parse a series of {@link android.R.styleable#Extra &lt;extra&gt;} tags from
2250      * an XML file.  You call this when you are at the parent tag of the
2251      * extra tags, and it will return once all of the child tags have been parsed.
2252      * This will call {@link #parseBundleExtra} for each extra tag encountered.
2253      *
2254      * @param parser The parser from which to retrieve the extras.
2255      * @param outBundle A Bundle in which to place all parsed extras.
2256      * @throws XmlPullParserException
2257      * @throws IOException
2258      */
parseBundleExtras(XmlResourceParser parser, Bundle outBundle)2259     public void parseBundleExtras(XmlResourceParser parser, Bundle outBundle)
2260             throws XmlPullParserException, IOException {
2261         int outerDepth = parser.getDepth();
2262         int type;
2263         while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
2264                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
2265             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
2266                 continue;
2267             }
2268 
2269             String nodeName = parser.getName();
2270             if (nodeName.equals("extra")) {
2271                 parseBundleExtra("extra", parser, outBundle);
2272                 XmlUtils.skipCurrentTag(parser);
2273 
2274             } else {
2275                 XmlUtils.skipCurrentTag(parser);
2276             }
2277         }
2278     }
2279 
2280     /**
2281      * Parse a name/value pair out of an XML tag holding that data.  The
2282      * AttributeSet must be holding the data defined by
2283      * {@link android.R.styleable#Extra}.  The following value types are supported:
2284      * <ul>
2285      * <li> {@link TypedValue#TYPE_STRING}:
2286      * {@link Bundle#putCharSequence Bundle.putCharSequence()}
2287      * <li> {@link TypedValue#TYPE_INT_BOOLEAN}:
2288      * {@link Bundle#putCharSequence Bundle.putBoolean()}
2289      * <li> {@link TypedValue#TYPE_FIRST_INT}-{@link TypedValue#TYPE_LAST_INT}:
2290      * {@link Bundle#putCharSequence Bundle.putBoolean()}
2291      * <li> {@link TypedValue#TYPE_FLOAT}:
2292      * {@link Bundle#putCharSequence Bundle.putFloat()}
2293      * </ul>
2294      *
2295      * @param tagName The name of the tag these attributes come from; this is
2296      * only used for reporting error messages.
2297      * @param attrs The attributes from which to retrieve the name/value pair.
2298      * @param outBundle The Bundle in which to place the parsed value.
2299      * @throws XmlPullParserException If the attributes are not valid.
2300      */
parseBundleExtra(String tagName, AttributeSet attrs, Bundle outBundle)2301     public void parseBundleExtra(String tagName, AttributeSet attrs,
2302             Bundle outBundle) throws XmlPullParserException {
2303         TypedArray sa = obtainAttributes(attrs,
2304                 com.android.internal.R.styleable.Extra);
2305 
2306         String name = sa.getString(
2307                 com.android.internal.R.styleable.Extra_name);
2308         if (name == null) {
2309             sa.recycle();
2310             throw new XmlPullParserException("<" + tagName
2311                     + "> requires an android:name attribute at "
2312                     + attrs.getPositionDescription());
2313         }
2314 
2315         TypedValue v = sa.peekValue(
2316                 com.android.internal.R.styleable.Extra_value);
2317         if (v != null) {
2318             if (v.type == TypedValue.TYPE_STRING) {
2319                 CharSequence cs = v.coerceToString();
2320                 outBundle.putCharSequence(name, cs);
2321             } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) {
2322                 outBundle.putBoolean(name, v.data != 0);
2323             } else if (v.type >= TypedValue.TYPE_FIRST_INT
2324                     && v.type <= TypedValue.TYPE_LAST_INT) {
2325                 outBundle.putInt(name, v.data);
2326             } else if (v.type == TypedValue.TYPE_FLOAT) {
2327                 outBundle.putFloat(name, v.getFloat());
2328             } else {
2329                 sa.recycle();
2330                 throw new XmlPullParserException("<" + tagName
2331                         + "> only supports string, integer, float, color, and boolean at "
2332                         + attrs.getPositionDescription());
2333             }
2334         } else {
2335             sa.recycle();
2336             throw new XmlPullParserException("<" + tagName
2337                     + "> requires an android:value or android:resource attribute at "
2338                     + attrs.getPositionDescription());
2339         }
2340 
2341         sa.recycle();
2342     }
2343 
2344     /**
2345      * Retrieve underlying AssetManager storage for these resources.
2346      */
getAssets()2347     public final AssetManager getAssets() {
2348         return mResourcesImpl.getAssets();
2349     }
2350 
2351     /**
2352      * Call this to remove all cached loaded layout resources from the
2353      * Resources object.  Only intended for use with performance testing
2354      * tools.
2355      */
flushLayoutCache()2356     public final void flushLayoutCache() {
2357         mResourcesImpl.flushLayoutCache();
2358     }
2359 
2360     /**
2361      * Start preloading of resource data using this Resources object.  Only
2362      * for use by the zygote process for loading common system resources.
2363      * {@hide}
2364      */
startPreloading()2365     public final void startPreloading() {
2366         mResourcesImpl.startPreloading();
2367     }
2368 
2369     /**
2370      * Called by zygote when it is done preloading resources, to change back
2371      * to normal Resources operation.
2372      */
finishPreloading()2373     public final void finishPreloading() {
2374         mResourcesImpl.finishPreloading();
2375     }
2376 
2377     /**
2378      * @hide
2379      */
2380     @UnsupportedAppUsage
getPreloadedDrawables()2381     public LongSparseArray<ConstantState> getPreloadedDrawables() {
2382         return mResourcesImpl.getPreloadedDrawables();
2383     }
2384 
2385     /**
2386      * Loads an XML parser for the specified file.
2387      *
2388      * @param id the resource identifier for the file
2389      * @param type the type of resource (used for logging)
2390      * @return a parser for the specified XML file
2391      * @throws NotFoundException if the file could not be loaded
2392      */
2393     @NonNull
2394     @UnsupportedAppUsage
loadXmlResourceParser(@nyRes int id, @NonNull String type)2395     XmlResourceParser loadXmlResourceParser(@AnyRes int id, @NonNull String type)
2396             throws NotFoundException {
2397         final TypedValue value = obtainTempTypedValue();
2398         try {
2399             final ResourcesImpl impl = mResourcesImpl;
2400             impl.getValue(id, value, true);
2401             if (value.type == TypedValue.TYPE_STRING) {
2402                 return loadXmlResourceParser(value.string.toString(), id,
2403                         value.assetCookie, type);
2404             }
2405             throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
2406                     + " type #0x" + Integer.toHexString(value.type) + " is not valid");
2407         } finally {
2408             releaseTempTypedValue(value);
2409         }
2410     }
2411 
2412     /**
2413      * Loads an XML parser for the specified file.
2414      *
2415      * @param file the path for the XML file to parse
2416      * @param id the resource identifier for the file
2417      * @param assetCookie the asset cookie for the file
2418      * @param type the type of resource (used for logging)
2419      * @return a parser for the specified XML file
2420      * @throws NotFoundException if the file could not be loaded
2421      */
2422     @NonNull
2423     @UnsupportedAppUsage
loadXmlResourceParser(String file, int id, int assetCookie, String type)2424     XmlResourceParser loadXmlResourceParser(String file, int id, int assetCookie,
2425                                             String type) throws NotFoundException {
2426         return mResourcesImpl.loadXmlResourceParser(file, id, assetCookie, type);
2427     }
2428 
2429     /**
2430      * Called by ConfigurationBoundResourceCacheTest.
2431      * @hide
2432      */
2433     @VisibleForTesting
calcConfigChanges(Configuration config)2434     public int calcConfigChanges(Configuration config) {
2435         return mResourcesImpl.calcConfigChanges(config);
2436     }
2437 
2438     /**
2439      * Obtains styled attributes from the theme, if available, or unstyled
2440      * resources if the theme is null.
2441      *
2442      * @hide
2443      */
obtainAttributes( Resources res, Theme theme, AttributeSet set, int[] attrs)2444     public static TypedArray obtainAttributes(
2445             Resources res, Theme theme, AttributeSet set, int[] attrs) {
2446         if (theme == null) {
2447             return res.obtainAttributes(set, attrs);
2448         }
2449         return theme.obtainStyledAttributes(set, attrs, 0, 0);
2450     }
2451 
checkCallbacksRegistered()2452     private void checkCallbacksRegistered() {
2453         if (mCallbacks == null) {
2454             // Fallback to updating the underlying AssetManager if the Resources is not associated
2455             // with a ResourcesManager.
2456             mCallbacks = new AssetManagerUpdateHandler();
2457         }
2458     }
2459 
2460     /**
2461      * Retrieves the list of loaders.
2462      *
2463      * <p>Loaders are listed in increasing precedence order. A loader will override the resources
2464      * and assets of loaders listed before itself.
2465      * @hide
2466      */
2467     @NonNull
getLoaders()2468     public List<ResourcesLoader> getLoaders() {
2469         return mResourcesImpl.getAssets().getLoaders();
2470     }
2471 
2472     /**
2473      * Adds a loader to the list of loaders. If the loader is already present in the list, the list
2474      * will not be modified.
2475      *
2476      * <p>This should only be called from the UI thread to avoid lock contention when propagating
2477      * loader changes.
2478      *
2479      * @param loaders the loaders to add
2480      */
addLoaders(@onNull ResourcesLoader... loaders)2481     public void addLoaders(@NonNull ResourcesLoader... loaders) {
2482         synchronized (mUpdateLock) {
2483             checkCallbacksRegistered();
2484             final List<ResourcesLoader> newLoaders =
2485                     new ArrayList<>(mResourcesImpl.getAssets().getLoaders());
2486             final ArraySet<ResourcesLoader> loaderSet = new ArraySet<>(newLoaders);
2487 
2488             for (int i = 0; i < loaders.length; i++) {
2489                 final ResourcesLoader loader = loaders[i];
2490                 if (!loaderSet.contains(loader)) {
2491                     newLoaders.add(loader);
2492                 }
2493             }
2494 
2495             if (loaderSet.size() == newLoaders.size()) {
2496                 return;
2497             }
2498 
2499             mCallbacks.onLoadersChanged(this, newLoaders);
2500             for (int i = loaderSet.size(), n = newLoaders.size(); i < n; i++) {
2501                 newLoaders.get(i).registerOnProvidersChangedCallback(this, mCallbacks);
2502             }
2503         }
2504     }
2505 
2506     /**
2507      * Removes loaders from the list of loaders. If the loader is not present in the list, the list
2508      * will not be modified.
2509      *
2510      * <p>This should only be called from the UI thread to avoid lock contention when propagating
2511      * loader changes.
2512      *
2513      * @param loaders the loaders to remove
2514      */
removeLoaders(@onNull ResourcesLoader... loaders)2515     public void removeLoaders(@NonNull ResourcesLoader... loaders) {
2516         synchronized (mUpdateLock) {
2517             checkCallbacksRegistered();
2518             final ArraySet<ResourcesLoader> removedLoaders = new ArraySet<>(loaders);
2519             final List<ResourcesLoader> newLoaders = new ArrayList<>();
2520             final List<ResourcesLoader> oldLoaders = mResourcesImpl.getAssets().getLoaders();
2521 
2522             for (int i = 0, n = oldLoaders.size(); i < n; i++) {
2523                 final ResourcesLoader loader = oldLoaders.get(i);
2524                 if (!removedLoaders.contains(loader)) {
2525                     newLoaders.add(loader);
2526                 }
2527             }
2528 
2529             if (oldLoaders.size() == newLoaders.size()) {
2530                 return;
2531             }
2532 
2533             mCallbacks.onLoadersChanged(this, newLoaders);
2534             for (int i = 0; i < loaders.length; i++) {
2535                 loaders[i].unregisterOnProvidersChangedCallback(this);
2536             }
2537         }
2538     }
2539 
2540     /**
2541      * Removes all {@link ResourcesLoader ResourcesLoader(s)}.
2542      *
2543      * <p>This should only be called from the UI thread to avoid lock contention when propagating
2544      * loader changes.
2545      * @hide
2546      */
2547     @VisibleForTesting
clearLoaders()2548     public void clearLoaders() {
2549         synchronized (mUpdateLock) {
2550             checkCallbacksRegistered();
2551             final List<ResourcesLoader> newLoaders = Collections.emptyList();
2552             final List<ResourcesLoader> oldLoaders = mResourcesImpl.getAssets().getLoaders();
2553             mCallbacks.onLoadersChanged(this, newLoaders);
2554             for (ResourcesLoader loader : oldLoaders) {
2555                 loader.unregisterOnProvidersChangedCallback(this);
2556             }
2557         }
2558     }
2559 }
2560