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