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