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