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