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