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