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