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