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