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