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