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