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