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 = DisplayMetrics.DENSITY_DEVICE; 697 } else { 698 value.density = (value.density * DisplayMetrics.DENSITY_DEVICE) / 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 {@link TypedArray#recycle() TypedArray.recycle()} when you are done 1124 * with 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 {@link TypedArray#recycle() TypedArray.recycle()} when you are done 1152 * with 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 {@link TypedArray#recycle() TypedArray.recycle()} when you are done 1212 * with 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(mTmpConfig); 1439 } 1440 if (mTmpConfig.locale == null) { 1441 mTmpConfig.locale = Locale.getDefault(); 1442 } 1443 configChanges = mConfiguration.updateFrom(mTmpConfig); 1444 configChanges = ActivityInfo.activityInfoConfigToNative(configChanges); 1445 } 1446 if (mConfiguration.locale == null) { 1447 mConfiguration.locale = Locale.getDefault(); 1448 } 1449 mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale; 1450 1451 String locale = null; 1452 if (mConfiguration.locale != null) { 1453 locale = mConfiguration.locale.getLanguage(); 1454 if (mConfiguration.locale.getCountry() != null) { 1455 locale += "-" + mConfiguration.locale.getCountry(); 1456 } 1457 } 1458 int width, height; 1459 if (mMetrics.widthPixels >= mMetrics.heightPixels) { 1460 width = mMetrics.widthPixels; 1461 height = mMetrics.heightPixels; 1462 } else { 1463 //noinspection SuspiciousNameCombination 1464 width = mMetrics.heightPixels; 1465 //noinspection SuspiciousNameCombination 1466 height = mMetrics.widthPixels; 1467 } 1468 int keyboardHidden = mConfiguration.keyboardHidden; 1469 if (keyboardHidden == Configuration.KEYBOARDHIDDEN_NO 1470 && mConfiguration.hardKeyboardHidden 1471 == Configuration.HARDKEYBOARDHIDDEN_YES) { 1472 keyboardHidden = Configuration.KEYBOARDHIDDEN_SOFT; 1473 } 1474 mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc, 1475 locale, mConfiguration.orientation, 1476 mConfiguration.touchscreen, 1477 (int)(mMetrics.density*160), mConfiguration.keyboard, 1478 keyboardHidden, mConfiguration.navigation, width, height, 1479 mConfiguration.smallestScreenWidthDp, 1480 mConfiguration.screenWidthDp, mConfiguration.screenHeightDp, 1481 mConfiguration.screenLayout, mConfiguration.uiMode, 1482 Build.VERSION.RESOURCES_SDK_INT); 1483 1484 if (DEBUG_CONFIG) { 1485 Slog.i(TAG, "**** Updating config of " + this + ": final config is " + mConfiguration 1486 + " final compat is " + mCompatibilityInfo); 1487 } 1488 1489 clearDrawableCache(mDrawableCache, configChanges); 1490 clearDrawableCache(mColorDrawableCache, configChanges); 1491 1492 mColorStateListCache.clear(); 1493 1494 flushLayoutCache(); 1495 } 1496 synchronized (mSync) { 1497 if (mPluralRule != null) { 1498 mPluralRule = NativePluralRules.forLocale(config.locale); 1499 } 1500 } 1501 } 1502 1503 private void clearDrawableCache( 1504 LongSparseArray<WeakReference<ConstantState>> cache, 1505 int configChanges) { 1506 int N = cache.size(); 1507 if (DEBUG_CONFIG) { 1508 Log.d(TAG, "Cleaning up drawables config changes: 0x" 1509 + Integer.toHexString(configChanges)); 1510 } 1511 for (int i=0; i<N; i++) { 1512 WeakReference<Drawable.ConstantState> ref = cache.valueAt(i); 1513 if (ref != null) { 1514 Drawable.ConstantState cs = ref.get(); 1515 if (cs != null) { 1516 if (Configuration.needNewResources( 1517 configChanges, cs.getChangingConfigurations())) { 1518 if (DEBUG_CONFIG) { 1519 Log.d(TAG, "FLUSHING #0x" 1520 + Long.toHexString(mDrawableCache.keyAt(i)) 1521 + " / " + cs + " with changes: 0x" 1522 + Integer.toHexString(cs.getChangingConfigurations())); 1523 } 1524 cache.setValueAt(i, null); 1525 } else if (DEBUG_CONFIG) { 1526 Log.d(TAG, "(Keeping #0x" 1527 + Long.toHexString(cache.keyAt(i)) 1528 + " / " + cs + " with changes: 0x" 1529 + Integer.toHexString(cs.getChangingConfigurations()) 1530 + ")"); 1531 } 1532 } 1533 } 1534 } 1535 } 1536 1537 /** 1538 * Update the system resources configuration if they have previously 1539 * been initialized. 1540 * 1541 * @hide 1542 */ 1543 public static void updateSystemConfiguration(Configuration config, DisplayMetrics metrics, 1544 CompatibilityInfo compat) { 1545 if (mSystem != null) { 1546 mSystem.updateConfiguration(config, metrics, compat); 1547 //Log.i(TAG, "Updated system resources " + mSystem 1548 // + ": " + mSystem.getConfiguration()); 1549 } 1550 } 1551 1552 /** 1553 * @hide 1554 */ 1555 public static void updateSystemConfiguration(Configuration config, DisplayMetrics metrics) { 1556 updateSystemConfiguration(config, metrics, null); 1557 } 1558 1559 /** 1560 * Return the current display metrics that are in effect for this resource 1561 * object. The returned object should be treated as read-only. 1562 * 1563 * @return The resource's current display metrics. 1564 */ 1565 public DisplayMetrics getDisplayMetrics() { 1566 if (DEBUG_CONFIG) Slog.v(TAG, "Returning DisplayMetrics: " + mMetrics.widthPixels 1567 + "x" + mMetrics.heightPixels + " " + mMetrics.density); 1568 return mMetrics; 1569 } 1570 1571 /** 1572 * Return the current configuration that is in effect for this resource 1573 * object. The returned object should be treated as read-only. 1574 * 1575 * @return The resource's current configuration. 1576 */ 1577 public Configuration getConfiguration() { 1578 return mConfiguration; 1579 } 1580 1581 /** 1582 * Return the compatibility mode information for the application. 1583 * The returned object should be treated as read-only. 1584 * 1585 * @return compatibility info. 1586 * @hide 1587 */ 1588 public CompatibilityInfo getCompatibilityInfo() { 1589 return mCompatibilityInfo != null ? mCompatibilityInfo 1590 : CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; 1591 } 1592 1593 /** 1594 * This is just for testing. 1595 * @hide 1596 */ 1597 public void setCompatibilityInfo(CompatibilityInfo ci) { 1598 mCompatibilityInfo = ci; 1599 updateConfiguration(mConfiguration, mMetrics); 1600 } 1601 1602 /** 1603 * Return a resource identifier for the given resource name. A fully 1604 * qualified resource name is of the form "package:type/entry". The first 1605 * two components (package and type) are optional if defType and 1606 * defPackage, respectively, are specified here. 1607 * 1608 * <p>Note: use of this function is discouraged. It is much more 1609 * efficient to retrieve resources by identifier than by name. 1610 * 1611 * @param name The name of the desired resource. 1612 * @param defType Optional default resource type to find, if "type/" is 1613 * not included in the name. Can be null to require an 1614 * explicit type. 1615 * @param defPackage Optional default package to find, if "package:" is 1616 * not included in the name. Can be null to require an 1617 * explicit package. 1618 * 1619 * @return int The associated resource identifier. Returns 0 if no such 1620 * resource was found. (0 is not a valid resource ID.) 1621 */ 1622 public int getIdentifier(String name, String defType, String defPackage) { 1623 try { 1624 return Integer.parseInt(name); 1625 } catch (Exception e) { 1626 // Ignore 1627 } 1628 return mAssets.getResourceIdentifier(name, defType, defPackage); 1629 } 1630 1631 /** 1632 * Return the full name for a given resource identifier. This name is 1633 * a single string of the form "package:type/entry". 1634 * 1635 * @param resid The resource identifier whose name is to be retrieved. 1636 * 1637 * @return A string holding the name of the resource. 1638 * 1639 * @throws NotFoundException Throws NotFoundException if the given ID does not exist. 1640 * 1641 * @see #getResourcePackageName 1642 * @see #getResourceTypeName 1643 * @see #getResourceEntryName 1644 */ 1645 public String getResourceName(int resid) throws NotFoundException { 1646 String str = mAssets.getResourceName(resid); 1647 if (str != null) return str; 1648 throw new NotFoundException("Unable to find resource ID #0x" 1649 + Integer.toHexString(resid)); 1650 } 1651 1652 /** 1653 * Return the package name for a given resource identifier. 1654 * 1655 * @param resid The resource identifier whose package name is to be 1656 * retrieved. 1657 * 1658 * @return A string holding the package name of the resource. 1659 * 1660 * @throws NotFoundException Throws NotFoundException if the given ID does not exist. 1661 * 1662 * @see #getResourceName 1663 */ 1664 public String getResourcePackageName(int resid) throws NotFoundException { 1665 String str = mAssets.getResourcePackageName(resid); 1666 if (str != null) return str; 1667 throw new NotFoundException("Unable to find resource ID #0x" 1668 + Integer.toHexString(resid)); 1669 } 1670 1671 /** 1672 * Return the type name for a given resource identifier. 1673 * 1674 * @param resid The resource identifier whose type name is to be 1675 * retrieved. 1676 * 1677 * @return A string holding the type name of the resource. 1678 * 1679 * @throws NotFoundException Throws NotFoundException if the given ID does not exist. 1680 * 1681 * @see #getResourceName 1682 */ 1683 public String getResourceTypeName(int resid) throws NotFoundException { 1684 String str = mAssets.getResourceTypeName(resid); 1685 if (str != null) return str; 1686 throw new NotFoundException("Unable to find resource ID #0x" 1687 + Integer.toHexString(resid)); 1688 } 1689 1690 /** 1691 * Return the entry name for a given resource identifier. 1692 * 1693 * @param resid The resource identifier whose entry name is to be 1694 * retrieved. 1695 * 1696 * @return A string holding the entry name of the resource. 1697 * 1698 * @throws NotFoundException Throws NotFoundException if the given ID does not exist. 1699 * 1700 * @see #getResourceName 1701 */ 1702 public String getResourceEntryName(int resid) throws NotFoundException { 1703 String str = mAssets.getResourceEntryName(resid); 1704 if (str != null) return str; 1705 throw new NotFoundException("Unable to find resource ID #0x" 1706 + Integer.toHexString(resid)); 1707 } 1708 1709 /** 1710 * Parse a series of {@link android.R.styleable#Extra <extra>} tags from 1711 * an XML file. You call this when you are at the parent tag of the 1712 * extra tags, and it will return once all of the child tags have been parsed. 1713 * This will call {@link #parseBundleExtra} for each extra tag encountered. 1714 * 1715 * @param parser The parser from which to retrieve the extras. 1716 * @param outBundle A Bundle in which to place all parsed extras. 1717 * @throws XmlPullParserException 1718 * @throws IOException 1719 */ 1720 public void parseBundleExtras(XmlResourceParser parser, Bundle outBundle) 1721 throws XmlPullParserException, IOException { 1722 int outerDepth = parser.getDepth(); 1723 int type; 1724 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 1725 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 1726 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 1727 continue; 1728 } 1729 1730 String nodeName = parser.getName(); 1731 if (nodeName.equals("extra")) { 1732 parseBundleExtra("extra", parser, outBundle); 1733 XmlUtils.skipCurrentTag(parser); 1734 1735 } else { 1736 XmlUtils.skipCurrentTag(parser); 1737 } 1738 } 1739 } 1740 1741 /** 1742 * Parse a name/value pair out of an XML tag holding that data. The 1743 * AttributeSet must be holding the data defined by 1744 * {@link android.R.styleable#Extra}. The following value types are supported: 1745 * <ul> 1746 * <li> {@link TypedValue#TYPE_STRING}: 1747 * {@link Bundle#putCharSequence Bundle.putCharSequence()} 1748 * <li> {@link TypedValue#TYPE_INT_BOOLEAN}: 1749 * {@link Bundle#putCharSequence Bundle.putBoolean()} 1750 * <li> {@link TypedValue#TYPE_FIRST_INT}-{@link TypedValue#TYPE_LAST_INT}: 1751 * {@link Bundle#putCharSequence Bundle.putBoolean()} 1752 * <li> {@link TypedValue#TYPE_FLOAT}: 1753 * {@link Bundle#putCharSequence Bundle.putFloat()} 1754 * </ul> 1755 * 1756 * @param tagName The name of the tag these attributes come from; this is 1757 * only used for reporting error messages. 1758 * @param attrs The attributes from which to retrieve the name/value pair. 1759 * @param outBundle The Bundle in which to place the parsed value. 1760 * @throws XmlPullParserException If the attributes are not valid. 1761 */ 1762 public void parseBundleExtra(String tagName, AttributeSet attrs, 1763 Bundle outBundle) throws XmlPullParserException { 1764 TypedArray sa = obtainAttributes(attrs, 1765 com.android.internal.R.styleable.Extra); 1766 1767 String name = sa.getString( 1768 com.android.internal.R.styleable.Extra_name); 1769 if (name == null) { 1770 sa.recycle(); 1771 throw new XmlPullParserException("<" + tagName 1772 + "> requires an android:name attribute at " 1773 + attrs.getPositionDescription()); 1774 } 1775 1776 TypedValue v = sa.peekValue( 1777 com.android.internal.R.styleable.Extra_value); 1778 if (v != null) { 1779 if (v.type == TypedValue.TYPE_STRING) { 1780 CharSequence cs = v.coerceToString(); 1781 outBundle.putCharSequence(name, cs); 1782 } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) { 1783 outBundle.putBoolean(name, v.data != 0); 1784 } else if (v.type >= TypedValue.TYPE_FIRST_INT 1785 && v.type <= TypedValue.TYPE_LAST_INT) { 1786 outBundle.putInt(name, v.data); 1787 } else if (v.type == TypedValue.TYPE_FLOAT) { 1788 outBundle.putFloat(name, v.getFloat()); 1789 } else { 1790 sa.recycle(); 1791 throw new XmlPullParserException("<" + tagName 1792 + "> only supports string, integer, float, color, and boolean at " 1793 + attrs.getPositionDescription()); 1794 } 1795 } else { 1796 sa.recycle(); 1797 throw new XmlPullParserException("<" + tagName 1798 + "> requires an android:value or android:resource attribute at " 1799 + attrs.getPositionDescription()); 1800 } 1801 1802 sa.recycle(); 1803 } 1804 1805 /** 1806 * Retrieve underlying AssetManager storage for these resources. 1807 */ 1808 public final AssetManager getAssets() { 1809 return mAssets; 1810 } 1811 1812 /** 1813 * Call this to remove all cached loaded layout resources from the 1814 * Resources object. Only intended for use with performance testing 1815 * tools. 1816 */ 1817 public final void flushLayoutCache() { 1818 synchronized (mCachedXmlBlockIds) { 1819 // First see if this block is in our cache. 1820 final int num = mCachedXmlBlockIds.length; 1821 for (int i=0; i<num; i++) { 1822 mCachedXmlBlockIds[i] = -0; 1823 XmlBlock oldBlock = mCachedXmlBlocks[i]; 1824 if (oldBlock != null) { 1825 oldBlock.close(); 1826 } 1827 mCachedXmlBlocks[i] = null; 1828 } 1829 } 1830 } 1831 1832 /** 1833 * Start preloading of resource data using this Resources object. Only 1834 * for use by the zygote process for loading common system resources. 1835 * {@hide} 1836 */ 1837 public final void startPreloading() { 1838 synchronized (mSync) { 1839 if (mPreloaded) { 1840 throw new IllegalStateException("Resources already preloaded"); 1841 } 1842 mPreloaded = true; 1843 mPreloading = true; 1844 } 1845 } 1846 1847 /** 1848 * Called by zygote when it is done preloading resources, to change back 1849 * to normal Resources operation. 1850 */ 1851 public final void finishPreloading() { 1852 if (mPreloading) { 1853 mPreloading = false; 1854 flushLayoutCache(); 1855 } 1856 } 1857 1858 /*package*/ Drawable loadDrawable(TypedValue value, int id) 1859 throws NotFoundException { 1860 1861 if (TRACE_FOR_PRELOAD) { 1862 // Log only framework resources 1863 if ((id >>> 24) == 0x1) { 1864 final String name = getResourceName(id); 1865 if (name != null) android.util.Log.d("PreloadDrawable", name); 1866 } 1867 } 1868 1869 final long key = (((long) value.assetCookie) << 32) | value.data; 1870 boolean isColorDrawable = false; 1871 if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT && 1872 value.type <= TypedValue.TYPE_LAST_COLOR_INT) { 1873 isColorDrawable = true; 1874 } 1875 Drawable dr = getCachedDrawable(isColorDrawable ? mColorDrawableCache : mDrawableCache, key); 1876 1877 if (dr != null) { 1878 return dr; 1879 } 1880 1881 Drawable.ConstantState cs = isColorDrawable ? 1882 sPreloadedColorDrawables.get(key) : sPreloadedDrawables.get(key); 1883 if (cs != null) { 1884 dr = cs.newDrawable(this); 1885 } else { 1886 if (isColorDrawable) { 1887 dr = new ColorDrawable(value.data); 1888 } 1889 1890 if (dr == null) { 1891 if (value.string == null) { 1892 throw new NotFoundException( 1893 "Resource is not a Drawable (color or path): " + value); 1894 } 1895 1896 String file = value.string.toString(); 1897 1898 if (TRACE_FOR_MISS_PRELOAD) { 1899 // Log only framework resources 1900 if ((id >>> 24) == 0x1) { 1901 final String name = getResourceName(id); 1902 if (name != null) android.util.Log.d(TAG, "Loading framework drawable #" 1903 + Integer.toHexString(id) + ": " + name 1904 + " at " + file); 1905 } 1906 } 1907 1908 if (DEBUG_LOAD) Log.v(TAG, "Loading drawable for cookie " 1909 + value.assetCookie + ": " + file); 1910 1911 if (file.endsWith(".xml")) { 1912 try { 1913 XmlResourceParser rp = loadXmlResourceParser( 1914 file, id, value.assetCookie, "drawable"); 1915 dr = Drawable.createFromXml(this, rp); 1916 rp.close(); 1917 } catch (Exception e) { 1918 NotFoundException rnf = new NotFoundException( 1919 "File " + file + " from drawable resource ID #0x" 1920 + Integer.toHexString(id)); 1921 rnf.initCause(e); 1922 throw rnf; 1923 } 1924 1925 } else { 1926 try { 1927 InputStream is = mAssets.openNonAsset( 1928 value.assetCookie, file, AssetManager.ACCESS_STREAMING); 1929 // System.out.println("Opened file " + file + ": " + is); 1930 dr = Drawable.createFromResourceStream(this, value, is, 1931 file, null); 1932 is.close(); 1933 // System.out.println("Created stream: " + dr); 1934 } catch (Exception e) { 1935 NotFoundException rnf = new NotFoundException( 1936 "File " + file + " from drawable resource ID #0x" 1937 + Integer.toHexString(id)); 1938 rnf.initCause(e); 1939 throw rnf; 1940 } 1941 } 1942 } 1943 } 1944 1945 if (dr != null) { 1946 dr.setChangingConfigurations(value.changingConfigurations); 1947 cs = dr.getConstantState(); 1948 if (cs != null) { 1949 if (mPreloading) { 1950 if (isColorDrawable) { 1951 sPreloadedColorDrawables.put(key, cs); 1952 } else { 1953 sPreloadedDrawables.put(key, cs); 1954 } 1955 } else { 1956 synchronized (mTmpValue) { 1957 //Log.i(TAG, "Saving cached drawable @ #" + 1958 // Integer.toHexString(key.intValue()) 1959 // + " in " + this + ": " + cs); 1960 if (isColorDrawable) { 1961 mColorDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs)); 1962 } else { 1963 mDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs)); 1964 } 1965 } 1966 } 1967 } 1968 } 1969 1970 return dr; 1971 } 1972 1973 private Drawable getCachedDrawable( 1974 LongSparseArray<WeakReference<ConstantState>> drawableCache, 1975 long key) { 1976 synchronized (mTmpValue) { 1977 WeakReference<Drawable.ConstantState> wr = drawableCache.get(key); 1978 if (wr != null) { // we have the key 1979 Drawable.ConstantState entry = wr.get(); 1980 if (entry != null) { 1981 //Log.i(TAG, "Returning cached drawable @ #" + 1982 // Integer.toHexString(((Integer)key).intValue()) 1983 // + " in " + this + ": " + entry); 1984 return entry.newDrawable(this); 1985 } 1986 else { // our entry has been purged 1987 drawableCache.delete(key); 1988 } 1989 } 1990 } 1991 return null; 1992 } 1993 1994 /*package*/ ColorStateList loadColorStateList(TypedValue value, int id) 1995 throws NotFoundException { 1996 if (TRACE_FOR_PRELOAD) { 1997 // Log only framework resources 1998 if ((id >>> 24) == 0x1) { 1999 final String name = getResourceName(id); 2000 if (name != null) android.util.Log.d("PreloadColorStateList", name); 2001 } 2002 } 2003 2004 final long key = (((long) value.assetCookie) << 32) | value.data; 2005 2006 ColorStateList csl; 2007 2008 if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT && 2009 value.type <= TypedValue.TYPE_LAST_COLOR_INT) { 2010 2011 csl = sPreloadedColorStateLists.get(key); 2012 if (csl != null) { 2013 return csl; 2014 } 2015 2016 csl = ColorStateList.valueOf(value.data); 2017 if (mPreloading) { 2018 sPreloadedColorStateLists.put(key, csl); 2019 } 2020 2021 return csl; 2022 } 2023 2024 csl = getCachedColorStateList(key); 2025 if (csl != null) { 2026 return csl; 2027 } 2028 2029 csl = sPreloadedColorStateLists.get(key); 2030 if (csl != null) { 2031 return csl; 2032 } 2033 2034 if (value.string == null) { 2035 throw new NotFoundException( 2036 "Resource is not a ColorStateList (color or path): " + value); 2037 } 2038 2039 String file = value.string.toString(); 2040 2041 if (file.endsWith(".xml")) { 2042 try { 2043 XmlResourceParser rp = loadXmlResourceParser( 2044 file, id, value.assetCookie, "colorstatelist"); 2045 csl = ColorStateList.createFromXml(this, rp); 2046 rp.close(); 2047 } catch (Exception e) { 2048 NotFoundException rnf = new NotFoundException( 2049 "File " + file + " from color state list resource ID #0x" 2050 + Integer.toHexString(id)); 2051 rnf.initCause(e); 2052 throw rnf; 2053 } 2054 } else { 2055 throw new NotFoundException( 2056 "File " + file + " from drawable resource ID #0x" 2057 + Integer.toHexString(id) + ": .xml extension required"); 2058 } 2059 2060 if (csl != null) { 2061 if (mPreloading) { 2062 sPreloadedColorStateLists.put(key, csl); 2063 } else { 2064 synchronized (mTmpValue) { 2065 //Log.i(TAG, "Saving cached color state list @ #" + 2066 // Integer.toHexString(key.intValue()) 2067 // + " in " + this + ": " + csl); 2068 mColorStateListCache.put(key, new WeakReference<ColorStateList>(csl)); 2069 } 2070 } 2071 } 2072 2073 return csl; 2074 } 2075 2076 private ColorStateList getCachedColorStateList(long key) { 2077 synchronized (mTmpValue) { 2078 WeakReference<ColorStateList> wr = mColorStateListCache.get(key); 2079 if (wr != null) { // we have the key 2080 ColorStateList entry = wr.get(); 2081 if (entry != null) { 2082 //Log.i(TAG, "Returning cached color state list @ #" + 2083 // Integer.toHexString(((Integer)key).intValue()) 2084 // + " in " + this + ": " + entry); 2085 return entry; 2086 } else { // our entry has been purged 2087 mColorStateListCache.delete(key); 2088 } 2089 } 2090 } 2091 return null; 2092 } 2093 2094 /*package*/ XmlResourceParser loadXmlResourceParser(int id, String type) 2095 throws NotFoundException { 2096 synchronized (mTmpValue) { 2097 TypedValue value = mTmpValue; 2098 getValue(id, value, true); 2099 if (value.type == TypedValue.TYPE_STRING) { 2100 return loadXmlResourceParser(value.string.toString(), id, 2101 value.assetCookie, type); 2102 } 2103 throw new NotFoundException( 2104 "Resource ID #0x" + Integer.toHexString(id) + " type #0x" 2105 + Integer.toHexString(value.type) + " is not valid"); 2106 } 2107 } 2108 2109 /*package*/ XmlResourceParser loadXmlResourceParser(String file, int id, 2110 int assetCookie, String type) throws NotFoundException { 2111 if (id != 0) { 2112 try { 2113 // These may be compiled... 2114 synchronized (mCachedXmlBlockIds) { 2115 // First see if this block is in our cache. 2116 final int num = mCachedXmlBlockIds.length; 2117 for (int i=0; i<num; i++) { 2118 if (mCachedXmlBlockIds[i] == id) { 2119 //System.out.println("**** REUSING XML BLOCK! id=" 2120 // + id + ", index=" + i); 2121 return mCachedXmlBlocks[i].newParser(); 2122 } 2123 } 2124 2125 // Not in the cache, create a new block and put it at 2126 // the next slot in the cache. 2127 XmlBlock block = mAssets.openXmlBlockAsset( 2128 assetCookie, file); 2129 if (block != null) { 2130 int pos = mLastCachedXmlBlockIndex+1; 2131 if (pos >= num) pos = 0; 2132 mLastCachedXmlBlockIndex = pos; 2133 XmlBlock oldBlock = mCachedXmlBlocks[pos]; 2134 if (oldBlock != null) { 2135 oldBlock.close(); 2136 } 2137 mCachedXmlBlockIds[pos] = id; 2138 mCachedXmlBlocks[pos] = block; 2139 //System.out.println("**** CACHING NEW XML BLOCK! id=" 2140 // + id + ", index=" + pos); 2141 return block.newParser(); 2142 } 2143 } 2144 } catch (Exception e) { 2145 NotFoundException rnf = new NotFoundException( 2146 "File " + file + " from xml type " + type + " resource ID #0x" 2147 + Integer.toHexString(id)); 2148 rnf.initCause(e); 2149 throw rnf; 2150 } 2151 } 2152 2153 throw new NotFoundException( 2154 "File " + file + " from xml type " + type + " resource ID #0x" 2155 + Integer.toHexString(id)); 2156 } 2157 2158 private TypedArray getCachedStyledAttributes(int len) { 2159 synchronized (mTmpValue) { 2160 TypedArray attrs = mCachedStyledAttributes; 2161 if (attrs != null) { 2162 mCachedStyledAttributes = null; 2163 if (DEBUG_ATTRIBUTES_CACHE) { 2164 mLastRetrievedAttrs = new RuntimeException("here"); 2165 mLastRetrievedAttrs.fillInStackTrace(); 2166 } 2167 2168 attrs.mLength = len; 2169 int fullLen = len * AssetManager.STYLE_NUM_ENTRIES; 2170 if (attrs.mData.length >= fullLen) { 2171 return attrs; 2172 } 2173 attrs.mData = new int[fullLen]; 2174 attrs.mIndices = new int[1+len]; 2175 return attrs; 2176 } 2177 if (DEBUG_ATTRIBUTES_CACHE) { 2178 RuntimeException here = new RuntimeException("here"); 2179 here.fillInStackTrace(); 2180 if (mLastRetrievedAttrs != null) { 2181 Log.i(TAG, "Allocated new TypedArray of " + len + " in " + this, here); 2182 Log.i(TAG, "Last retrieved attributes here", mLastRetrievedAttrs); 2183 } 2184 mLastRetrievedAttrs = here; 2185 } 2186 return new TypedArray(this, 2187 new int[len*AssetManager.STYLE_NUM_ENTRIES], 2188 new int[1+len], len); 2189 } 2190 } 2191 2192 private Resources() { 2193 mAssets = AssetManager.getSystem(); 2194 // NOTE: Intentionally leaving this uninitialized (all values set 2195 // to zero), so that anyone who tries to do something that requires 2196 // metrics will get a very wrong value. 2197 mConfiguration.setToDefaults(); 2198 mMetrics.setToDefaults(); 2199 updateConfiguration(null, null); 2200 mAssets.ensureStringBlocks(); 2201 mCompatibilityInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; 2202 } 2203} 2204