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