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