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