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