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