Resources.java revision 48276ab989a4d775961ce30a43635a317052672a
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 file -- 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 public InputStream openRawResource(int id, TypedValue value) throws NotFoundException { 771 getValue(id, value, true); 772 773 try { 774 return mAssets.openNonAsset(value.assetCookie, value.string.toString(), 775 AssetManager.ACCESS_STREAMING); 776 } catch (Exception e) { 777 NotFoundException rnf = new NotFoundException("File " + value.string.toString() + 778 " from drawable resource ID #0x" + Integer.toHexString(id)); 779 rnf.initCause(e); 780 throw rnf; 781 } 782 } 783 784 /** 785 * Open a file descriptor for reading a raw resource. This can only be used 786 * with resources whose value is the name of an asset files -- that is, it can be 787 * used to open drawable, sound, and raw resources; it will fail on string 788 * and color resources. 789 * 790 * <p>This function only works for resources that are stored in the package 791 * as uncompressed data, which typically includes things like mp3 files 792 * and png images. 793 * 794 * @param id The resource identifier to open, as generated by the appt 795 * tool. 796 * 797 * @return AssetFileDescriptor A new file descriptor you can use to read 798 * the resource. This includes the file descriptor itself, as well as the 799 * offset and length of data where the resource appears in the file. A 800 * null is returned if the file exists but is compressed. 801 * 802 * @throws NotFoundException Throws NotFoundException if the given ID does not exist. 803 * 804 */ 805 public AssetFileDescriptor openRawResourceFd(int id) throws NotFoundException { 806 synchronized (mTmpValue) { 807 TypedValue value = mTmpValue; 808 getValue(id, value, true); 809 810 try { 811 return mAssets.openNonAssetFd( 812 value.assetCookie, value.string.toString()); 813 } catch (Exception e) { 814 NotFoundException rnf = new NotFoundException( 815 "File " + value.string.toString() 816 + " from drawable resource ID #0x" 817 + Integer.toHexString(id)); 818 rnf.initCause(e); 819 throw rnf; 820 } 821 822 } 823 } 824 825 /** 826 * Return the raw data associated with a particular resource ID. 827 * 828 * @param id The desired resource identifier, as generated by the aapt 829 * tool. This integer encodes the package, type, and resource 830 * entry. The value 0 is an invalid identifier. 831 * @param outValue Object in which to place the resource data. 832 * @param resolveRefs If true, a resource that is a reference to another 833 * resource will be followed so that you receive the 834 * actual final resource data. If false, the TypedValue 835 * will be filled in with the reference itself. 836 * 837 * @throws NotFoundException Throws NotFoundException if the given ID does not exist. 838 * 839 */ 840 public void getValue(int id, TypedValue outValue, boolean resolveRefs) 841 throws NotFoundException { 842 boolean found = mAssets.getResourceValue(id, outValue, resolveRefs); 843 if (found) { 844 return; 845 } 846 throw new NotFoundException("Resource ID #0x" 847 + Integer.toHexString(id)); 848 } 849 850 /** 851 * Return the raw data associated with a particular resource ID. 852 * See getIdentifier() for information on how names are mapped to resource 853 * IDs, and getString(int) for information on how string resources are 854 * retrieved. 855 * 856 * <p>Note: use of this function is discouraged. It is much more 857 * efficient to retrieve resources by identifier than by name. 858 * 859 * @param name The name of the desired resource. This is passed to 860 * getIdentifier() with a default type of "string". 861 * @param outValue Object in which to place the resource data. 862 * @param resolveRefs If true, a resource that is a reference to another 863 * resource will be followed so that you receive the 864 * actual final resource data. If false, the TypedValue 865 * will be filled in with the reference itself. 866 * 867 * @throws NotFoundException Throws NotFoundException if the given ID does not exist. 868 * 869 */ 870 public void getValue(String name, TypedValue outValue, boolean resolveRefs) 871 throws NotFoundException { 872 int id = getIdentifier(name, "string", null); 873 if (id != 0) { 874 getValue(id, outValue, resolveRefs); 875 return; 876 } 877 throw new NotFoundException("String resource name " + name); 878 } 879 880 /** 881 * This class holds the current attribute values for a particular theme. 882 * In other words, a Theme is a set of values for resource attributes; 883 * these are used in conjunction with {@link TypedArray} 884 * to resolve the final value for an attribute. 885 * 886 * <p>The Theme's attributes come into play in two ways: (1) a styled 887 * attribute can explicit reference a value in the theme through the 888 * "?themeAttribute" syntax; (2) if no value has been defined for a 889 * particular styled attribute, as a last resort we will try to find that 890 * attribute's value in the Theme. 891 * 892 * <p>You will normally use the {@link #obtainStyledAttributes} APIs to 893 * retrieve XML attributes with style and theme information applied. 894 */ 895 public final class Theme { 896 /** 897 * Place new attribute values into the theme. The style resource 898 * specified by <var>resid</var> will be retrieved from this Theme's 899 * resources, its values placed into the Theme object. 900 * 901 * <p>The semantics of this function depends on the <var>force</var> 902 * argument: If false, only values that are not already defined in 903 * the theme will be copied from the system resource; otherwise, if 904 * any of the style's attributes are already defined in the theme, the 905 * current values in the theme will be overwritten. 906 * 907 * @param resid The resource ID of a style resource from which to 908 * obtain attribute values. 909 * @param force If true, values in the style resource will always be 910 * used in the theme; otherwise, they will only be used 911 * if not already defined in the theme. 912 */ 913 public void applyStyle(int resid, boolean force) { 914 AssetManager.applyThemeStyle(mTheme, resid, force); 915 } 916 917 /** 918 * Set this theme to hold the same contents as the theme 919 * <var>other</var>. If both of these themes are from the same 920 * Resources object, they will be identical after this function 921 * returns. If they are from different Resources, only the resources 922 * they have in common will be set in this theme. 923 * 924 * @param other The existing Theme to copy from. 925 */ 926 public void setTo(Theme other) { 927 AssetManager.copyTheme(mTheme, other.mTheme); 928 } 929 930 /** 931 * Return a StyledAttributes holding the values defined by 932 * <var>Theme</var> which are listed in <var>attrs</var>. 933 * 934 * <p>Be sure to call StyledAttributes.recycle() when you are done with 935 * the array. 936 * 937 * @param attrs The desired attributes. 938 * 939 * @throws NotFoundException Throws NotFoundException if the given ID does not exist. 940 * 941 * @return Returns a TypedArray holding an array of the attribute values. 942 * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()} 943 * when done with it. 944 * 945 * @see Resources#obtainAttributes 946 * @see #obtainStyledAttributes(int, int[]) 947 * @see #obtainStyledAttributes(AttributeSet, int[], int, int) 948 */ 949 public TypedArray obtainStyledAttributes(int[] attrs) { 950 int len = attrs.length; 951 TypedArray array = getCachedStyledAttributes(len); 952 array.mRsrcs = attrs; 953 AssetManager.applyStyle(mTheme, 0, 0, 0, attrs, 954 array.mData, array.mIndices); 955 return array; 956 } 957 958 /** 959 * Return a StyledAttributes holding the values defined by the style 960 * resource <var>resid</var> which are listed in <var>attrs</var>. 961 * 962 * <p>Be sure to call StyledAttributes.recycle() when you are done with 963 * the array. 964 * 965 * @param resid The desired style resource. 966 * @param attrs The desired attributes in the style. 967 * 968 * @throws NotFoundException Throws NotFoundException if the given ID does not exist. 969 * 970 * @return Returns a TypedArray holding an array of the attribute values. 971 * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()} 972 * when done with it. 973 * 974 * @see Resources#obtainAttributes 975 * @see #obtainStyledAttributes(int[]) 976 * @see #obtainStyledAttributes(AttributeSet, int[], int, int) 977 */ 978 public TypedArray obtainStyledAttributes(int resid, int[] attrs) 979 throws NotFoundException { 980 int len = attrs.length; 981 TypedArray array = getCachedStyledAttributes(len); 982 array.mRsrcs = attrs; 983 984 AssetManager.applyStyle(mTheme, 0, resid, 0, attrs, 985 array.mData, array.mIndices); 986 if (false) { 987 int[] data = array.mData; 988 989 System.out.println("**********************************************************"); 990 System.out.println("**********************************************************"); 991 System.out.println("**********************************************************"); 992 System.out.println("Attributes:"); 993 String s = " Attrs:"; 994 int i; 995 for (i=0; i<attrs.length; i++) { 996 s = s + " 0x" + Integer.toHexString(attrs[i]); 997 } 998 System.out.println(s); 999 s = " Found:"; 1000 TypedValue value = new TypedValue(); 1001 for (i=0; i<attrs.length; i++) { 1002 int d = i*AssetManager.STYLE_NUM_ENTRIES; 1003 value.type = data[d+AssetManager.STYLE_TYPE]; 1004 value.data = data[d+AssetManager.STYLE_DATA]; 1005 value.assetCookie = data[d+AssetManager.STYLE_ASSET_COOKIE]; 1006 value.resourceId = data[d+AssetManager.STYLE_RESOURCE_ID]; 1007 s = s + " 0x" + Integer.toHexString(attrs[i]) 1008 + "=" + value; 1009 } 1010 System.out.println(s); 1011 } 1012 return array; 1013 } 1014 1015 /** 1016 * Return a StyledAttributes holding the attribute values in 1017 * <var>set</var> 1018 * that are listed in <var>attrs</var>. In addition, if the given 1019 * AttributeSet specifies a style class (through the "style" attribute), 1020 * that style will be applied on top of the base attributes it defines. 1021 * 1022 * <p>Be sure to call StyledAttributes.recycle() when you are done with 1023 * the array. 1024 * 1025 * <p>When determining the final value of a particular attribute, there 1026 * are four inputs that come into play:</p> 1027 * 1028 * <ol> 1029 * <li> Any attribute values in the given AttributeSet. 1030 * <li> The style resource specified in the AttributeSet (named 1031 * "style"). 1032 * <li> The default style specified by <var>defStyleAttr</var> and 1033 * <var>defStyleRes</var> 1034 * <li> The base values in this theme. 1035 * </ol> 1036 * 1037 * <p>Each of these inputs is considered in-order, with the first listed 1038 * taking precedence over the following ones. In other words, if in the 1039 * AttributeSet you have supplied <code><Button 1040 * textColor="#ff000000"></code>, then the button's text will 1041 * <em>always</em> be black, regardless of what is specified in any of 1042 * the styles. 1043 * 1044 * @param set The base set of attribute values. May be null. 1045 * @param attrs The desired attributes to be retrieved. 1046 * @param defStyleAttr An attribute in the current theme that contains a 1047 * reference to a style resource that supplies 1048 * defaults values for the StyledAttributes. Can be 1049 * 0 to not look for defaults. 1050 * @param defStyleRes A resource identifier of a style resource that 1051 * supplies default values for the StyledAttributes, 1052 * used only if defStyleAttr is 0 or can not be found 1053 * in the theme. Can be 0 to not look for defaults. 1054 * 1055 * @return Returns a TypedArray holding an array of the attribute values. 1056 * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()} 1057 * when done with it. 1058 * 1059 * @see Resources#obtainAttributes 1060 * @see #obtainStyledAttributes(int[]) 1061 * @see #obtainStyledAttributes(int, int[]) 1062 */ 1063 public TypedArray obtainStyledAttributes(AttributeSet set, 1064 int[] attrs, int defStyleAttr, int defStyleRes) { 1065 int len = attrs.length; 1066 TypedArray array = getCachedStyledAttributes(len); 1067 1068 // XXX note that for now we only work with compiled XML files. 1069 // To support generic XML files we will need to manually parse 1070 // out the attributes from the XML file (applying type information 1071 // contained in the resources and such). 1072 XmlBlock.Parser parser = (XmlBlock.Parser)set; 1073 AssetManager.applyStyle( 1074 mTheme, defStyleAttr, defStyleRes, 1075 parser != null ? parser.mParseState : 0, attrs, 1076 array.mData, array.mIndices); 1077 1078 array.mRsrcs = attrs; 1079 array.mXml = parser; 1080 1081 if (false) { 1082 int[] data = array.mData; 1083 1084 System.out.println("Attributes:"); 1085 String s = " Attrs:"; 1086 int i; 1087 for (i=0; i<set.getAttributeCount(); i++) { 1088 s = s + " " + set.getAttributeName(i); 1089 int id = set.getAttributeNameResource(i); 1090 if (id != 0) { 1091 s = s + "(0x" + Integer.toHexString(id) + ")"; 1092 } 1093 s = s + "=" + set.getAttributeValue(i); 1094 } 1095 System.out.println(s); 1096 s = " Found:"; 1097 TypedValue value = new TypedValue(); 1098 for (i=0; i<attrs.length; i++) { 1099 int d = i*AssetManager.STYLE_NUM_ENTRIES; 1100 value.type = data[d+AssetManager.STYLE_TYPE]; 1101 value.data = data[d+AssetManager.STYLE_DATA]; 1102 value.assetCookie = data[d+AssetManager.STYLE_ASSET_COOKIE]; 1103 value.resourceId = data[d+AssetManager.STYLE_RESOURCE_ID]; 1104 s = s + " 0x" + Integer.toHexString(attrs[i]) 1105 + "=" + value; 1106 } 1107 System.out.println(s); 1108 } 1109 1110 return array; 1111 } 1112 1113 /** 1114 * Retrieve the value of an attribute in the Theme. The contents of 1115 * <var>outValue</var> are ultimately filled in by 1116 * {@link Resources#getValue}. 1117 * 1118 * @param resid The resource identifier of the desired theme 1119 * attribute. 1120 * @param outValue Filled in with the ultimate resource value supplied 1121 * by the attribute. 1122 * @param resolveRefs If true, resource references will be walked; if 1123 * false, <var>outValue</var> may be a 1124 * TYPE_REFERENCE. In either case, it will never 1125 * be a TYPE_ATTRIBUTE. 1126 * 1127 * @return boolean Returns true if the attribute was found and 1128 * <var>outValue</var> is valid, else false. 1129 */ 1130 public boolean resolveAttribute(int resid, TypedValue outValue, 1131 boolean resolveRefs) { 1132 boolean got = mAssets.getThemeValue(mTheme, resid, outValue, resolveRefs); 1133 if (false) { 1134 System.out.println( 1135 "resolveAttribute #" + Integer.toHexString(resid) 1136 + " got=" + got + ", type=0x" + Integer.toHexString(outValue.type) 1137 + ", data=0x" + Integer.toHexString(outValue.data)); 1138 } 1139 return got; 1140 } 1141 1142 /** 1143 * Print contents of this theme out to the log. For debugging only. 1144 * 1145 * @param priority The log priority to use. 1146 * @param tag The log tag to use. 1147 * @param prefix Text to prefix each line printed. 1148 */ 1149 public void dump(int priority, String tag, String prefix) { 1150 AssetManager.dumpTheme(mTheme, priority, tag, prefix); 1151 } 1152 1153 protected void finalize() throws Throwable { 1154 super.finalize(); 1155 mAssets.releaseTheme(mTheme); 1156 } 1157 1158 /*package*/ Theme() { 1159 mAssets = Resources.this.mAssets; 1160 mTheme = mAssets.createTheme(); 1161 } 1162 1163 private final AssetManager mAssets; 1164 private final int mTheme; 1165 } 1166 1167 /** 1168 * Generate a new Theme object for this set of Resources. It initially 1169 * starts out empty. 1170 * 1171 * @return Theme The newly created Theme container. 1172 */ 1173 public final Theme newTheme() { 1174 return new Theme(); 1175 } 1176 1177 /** 1178 * Retrieve a set of basic attribute values from an AttributeSet, not 1179 * performing styling of them using a theme and/or style resources. 1180 * 1181 * @param set The current attribute values to retrieve. 1182 * @param attrs The specific attributes to be retrieved. 1183 * @return Returns a TypedArray holding an array of the attribute values. 1184 * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()} 1185 * when done with it. 1186 * 1187 * @see Theme#obtainStyledAttributes(AttributeSet, int[], int, int) 1188 */ 1189 public TypedArray obtainAttributes(AttributeSet set, int[] attrs) { 1190 int len = attrs.length; 1191 TypedArray array = getCachedStyledAttributes(len); 1192 1193 // XXX note that for now we only work with compiled XML files. 1194 // To support generic XML files we will need to manually parse 1195 // out the attributes from the XML file (applying type information 1196 // contained in the resources and such). 1197 XmlBlock.Parser parser = (XmlBlock.Parser)set; 1198 mAssets.retrieveAttributes(parser.mParseState, attrs, 1199 array.mData, array.mIndices); 1200 1201 array.mRsrcs = attrs; 1202 array.mXml = parser; 1203 1204 return array; 1205 } 1206 1207 /** 1208 * Store the newly updated configuration. 1209 */ 1210 public void updateConfiguration(Configuration config, 1211 DisplayMetrics metrics) { 1212 synchronized (mTmpValue) { 1213 int configChanges = 0xfffffff; 1214 if (config != null) { 1215 configChanges = mConfiguration.updateFrom(config); 1216 } 1217 if (metrics != null) { 1218 mMetrics.setTo(metrics); 1219 } 1220 mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale; 1221 String locale = null; 1222 if (mConfiguration.locale != null) { 1223 locale = mConfiguration.locale.getLanguage(); 1224 if (mConfiguration.locale.getCountry() != null) { 1225 locale += "-" + mConfiguration.locale.getCountry(); 1226 } 1227 } 1228 int width, height; 1229 if (mMetrics.widthPixels >= mMetrics.heightPixels) { 1230 width = mMetrics.widthPixels; 1231 height = mMetrics.heightPixels; 1232 } else { 1233 //noinspection SuspiciousNameCombination 1234 width = mMetrics.heightPixels; 1235 //noinspection SuspiciousNameCombination 1236 height = mMetrics.widthPixels; 1237 } 1238 int keyboardHidden = mConfiguration.keyboardHidden; 1239 if (keyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO 1240 && mConfiguration.hardKeyboardHidden 1241 == Configuration.HARDKEYBOARDHIDDEN_YES) { 1242 keyboardHidden = Configuration.KEYBOARDHIDDEN_SOFT; 1243 } 1244 mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc, 1245 locale, mConfiguration.orientation, 1246 mConfiguration.touchscreen, 1247 (int)(mMetrics.density*160), mConfiguration.keyboard, 1248 keyboardHidden, mConfiguration.navigation, width, height, 1249 sSdkVersion); 1250 int N = mDrawableCache.size(); 1251 if (DEBUG_CONFIG) { 1252 Log.d(TAG, "Cleaning up drawables config changes: 0x" 1253 + Integer.toHexString(configChanges)); 1254 } 1255 for (int i=0; i<N; i++) { 1256 WeakReference<Drawable.ConstantState> ref = mDrawableCache.valueAt(i); 1257 if (ref != null) { 1258 Drawable.ConstantState cs = ref.get(); 1259 if (cs != null) { 1260 if (Configuration.needNewResources( 1261 configChanges, cs.getChangingConfigurations())) { 1262 if (DEBUG_CONFIG) { 1263 Log.d(TAG, "FLUSHING #0x" 1264 + Integer.toHexString(mDrawableCache.keyAt(i)) 1265 + " / " + cs + " with changes: 0x" 1266 + Integer.toHexString(cs.getChangingConfigurations())); 1267 } 1268 mDrawableCache.setValueAt(i, null); 1269 } else if (DEBUG_CONFIG) { 1270 Log.d(TAG, "(Keeping #0x" 1271 + Integer.toHexString(mDrawableCache.keyAt(i)) 1272 + " / " + cs + " with changes: 0x" 1273 + Integer.toHexString(cs.getChangingConfigurations()) 1274 + ")"); 1275 } 1276 } 1277 } 1278 } 1279 mDrawableCache.clear(); 1280 mColorStateListCache.clear(); 1281 flushLayoutCache(); 1282 } 1283 synchronized (mSync) { 1284 if (mPluralRule != null) { 1285 mPluralRule = PluralRules.ruleForLocale(config.locale); 1286 } 1287 } 1288 } 1289 1290 /** 1291 * Update the system resources configuration if they have previously 1292 * been initialized. 1293 * 1294 * @hide 1295 */ 1296 public static void updateSystemConfiguration(Configuration config, DisplayMetrics metrics) { 1297 if (mSystem != null) { 1298 mSystem.updateConfiguration(config, metrics); 1299 //Log.i(TAG, "Updated system resources " + mSystem 1300 // + ": " + mSystem.getConfiguration()); 1301 } 1302 } 1303 1304 /** 1305 * Return the current display metrics that are in effect for this resource 1306 * object. The returned object should be treated as read-only. 1307 * 1308 * @return The resource's current display metrics. 1309 */ 1310 public DisplayMetrics getDisplayMetrics() { 1311 return mMetrics; 1312 } 1313 1314 /** 1315 * Return the current configuration that is in effect for this resource 1316 * object. The returned object should be treated as read-only. 1317 * 1318 * @return The resource's current configuration. 1319 */ 1320 public Configuration getConfiguration() { 1321 return mConfiguration; 1322 } 1323 1324 /** 1325 * Return a resource identifier for the given resource name. A fully 1326 * qualified resource name is of the form "package:type/entry". The first 1327 * two components (package and type) are optional if defType and 1328 * defPackage, respectively, are specified here. 1329 * 1330 * <p>Note: use of this function is discouraged. It is much more 1331 * efficient to retrieve resources by identifier than by name. 1332 * 1333 * @param name The name of the desired resource. 1334 * @param defType Optional default resource type to find, if "type/" is 1335 * not included in the name. Can be null to require an 1336 * explicit type. 1337 * @param defPackage Optional default package to find, if "package:" is 1338 * not included in the name. Can be null to require an 1339 * explicit package. 1340 * 1341 * @return int The associated resource identifier. Returns 0 if no such 1342 * resource was found. (0 is not a valid resource ID.) 1343 */ 1344 public int getIdentifier(String name, String defType, String defPackage) { 1345 try { 1346 return Integer.parseInt(name); 1347 } catch (Exception e) { 1348 // Ignore 1349 } 1350 return mAssets.getResourceIdentifier(name, defType, defPackage); 1351 } 1352 1353 /** 1354 * Return the full name for a given resource identifier. This name is 1355 * a single string of the form "package:type/entry". 1356 * 1357 * @param resid The resource identifier whose name is to be retrieved. 1358 * 1359 * @return A string holding the name of the resource. 1360 * 1361 * @throws NotFoundException Throws NotFoundException if the given ID does not exist. 1362 * 1363 * @see #getResourcePackageName 1364 * @see #getResourceTypeName 1365 * @see #getResourceEntryName 1366 */ 1367 public String getResourceName(int resid) throws NotFoundException { 1368 String str = mAssets.getResourceName(resid); 1369 if (str != null) return str; 1370 throw new NotFoundException("Unable to find resource ID #0x" 1371 + Integer.toHexString(resid)); 1372 } 1373 1374 /** 1375 * Return the package name for a given resource identifier. 1376 * 1377 * @param resid The resource identifier whose package name is to be 1378 * retrieved. 1379 * 1380 * @return A string holding the package name of the resource. 1381 * 1382 * @throws NotFoundException Throws NotFoundException if the given ID does not exist. 1383 * 1384 * @see #getResourceName 1385 */ 1386 public String getResourcePackageName(int resid) throws NotFoundException { 1387 String str = mAssets.getResourcePackageName(resid); 1388 if (str != null) return str; 1389 throw new NotFoundException("Unable to find resource ID #0x" 1390 + Integer.toHexString(resid)); 1391 } 1392 1393 /** 1394 * Return the type name for a given resource identifier. 1395 * 1396 * @param resid The resource identifier whose type name is to be 1397 * retrieved. 1398 * 1399 * @return A string holding the type name of the resource. 1400 * 1401 * @throws NotFoundException Throws NotFoundException if the given ID does not exist. 1402 * 1403 * @see #getResourceName 1404 */ 1405 public String getResourceTypeName(int resid) throws NotFoundException { 1406 String str = mAssets.getResourceTypeName(resid); 1407 if (str != null) return str; 1408 throw new NotFoundException("Unable to find resource ID #0x" 1409 + Integer.toHexString(resid)); 1410 } 1411 1412 /** 1413 * Return the entry name for a given resource identifier. 1414 * 1415 * @param resid The resource identifier whose entry name is to be 1416 * retrieved. 1417 * 1418 * @return A string holding the entry name of the resource. 1419 * 1420 * @throws NotFoundException Throws NotFoundException if the given ID does not exist. 1421 * 1422 * @see #getResourceName 1423 */ 1424 public String getResourceEntryName(int resid) throws NotFoundException { 1425 String str = mAssets.getResourceEntryName(resid); 1426 if (str != null) return str; 1427 throw new NotFoundException("Unable to find resource ID #0x" 1428 + Integer.toHexString(resid)); 1429 } 1430 1431 /** 1432 * Parse a series of {@link android.R.styleable#Extra <extra>} tags from 1433 * an XML file. You call this when you are at the parent tag of the 1434 * extra tags, and it return once all of the child tags have been parsed. 1435 * This will call {@link #parseBundleExtra} for each extra tag encountered. 1436 * 1437 * @param parser The parser from which to retrieve the extras. 1438 * @param outBundle A Bundle in which to place all parsed extras. 1439 * @throws XmlPullParserException 1440 * @throws IOException 1441 */ 1442 public void parseBundleExtras(XmlResourceParser parser, Bundle outBundle) 1443 throws XmlPullParserException, IOException { 1444 int outerDepth = parser.getDepth(); 1445 int type; 1446 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 1447 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 1448 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 1449 continue; 1450 } 1451 1452 String nodeName = parser.getName(); 1453 if (nodeName.equals("extra")) { 1454 parseBundleExtra("extra", parser, outBundle); 1455 XmlUtils.skipCurrentTag(parser); 1456 1457 } else { 1458 XmlUtils.skipCurrentTag(parser); 1459 } 1460 } 1461 } 1462 1463 /** 1464 * Parse a name/value pair out of an XML tag holding that data. The 1465 * AttributeSet must be holding the data defined by 1466 * {@link android.R.styleable#Extra}. The following value types are supported: 1467 * <ul> 1468 * <li> {@link TypedValue#TYPE_STRING}: 1469 * {@link Bundle#putCharSequence Bundle.putCharSequence()} 1470 * <li> {@link TypedValue#TYPE_INT_BOOLEAN}: 1471 * {@link Bundle#putCharSequence Bundle.putBoolean()} 1472 * <li> {@link TypedValue#TYPE_FIRST_INT}-{@link TypedValue#TYPE_LAST_INT}: 1473 * {@link Bundle#putCharSequence Bundle.putBoolean()} 1474 * <li> {@link TypedValue#TYPE_FLOAT}: 1475 * {@link Bundle#putCharSequence Bundle.putFloat()} 1476 * </ul> 1477 * 1478 * @param tagName The name of the tag these attributes come from; this is 1479 * only used for reporting error messages. 1480 * @param attrs The attributes from which to retrieve the name/value pair. 1481 * @param outBundle The Bundle in which to place the parsed value. 1482 * @throws XmlPullParserException If the attributes are not valid. 1483 */ 1484 public void parseBundleExtra(String tagName, AttributeSet attrs, 1485 Bundle outBundle) throws XmlPullParserException { 1486 TypedArray sa = obtainAttributes(attrs, 1487 com.android.internal.R.styleable.Extra); 1488 1489 String name = sa.getString( 1490 com.android.internal.R.styleable.Extra_name); 1491 if (name == null) { 1492 sa.recycle(); 1493 throw new XmlPullParserException("<" + tagName 1494 + "> requires an android:name attribute at " 1495 + attrs.getPositionDescription()); 1496 } 1497 1498 TypedValue v = sa.peekValue( 1499 com.android.internal.R.styleable.Extra_value); 1500 if (v != null) { 1501 if (v.type == TypedValue.TYPE_STRING) { 1502 CharSequence cs = v.coerceToString(); 1503 outBundle.putCharSequence(name, cs); 1504 } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) { 1505 outBundle.putBoolean(name, v.data != 0); 1506 } else if (v.type >= TypedValue.TYPE_FIRST_INT 1507 && v.type <= TypedValue.TYPE_LAST_INT) { 1508 outBundle.putInt(name, v.data); 1509 } else if (v.type == TypedValue.TYPE_FLOAT) { 1510 outBundle.putFloat(name, v.getFloat()); 1511 } else { 1512 sa.recycle(); 1513 throw new XmlPullParserException("<" + tagName 1514 + "> only supports string, integer, float, color, and boolean at " 1515 + attrs.getPositionDescription()); 1516 } 1517 } else { 1518 sa.recycle(); 1519 throw new XmlPullParserException("<" + tagName 1520 + "> requires an android:value or android:resource attribute at " 1521 + attrs.getPositionDescription()); 1522 } 1523 1524 sa.recycle(); 1525 } 1526 1527 /** 1528 * Retrieve underlying AssetManager storage for these resources. 1529 */ 1530 public final AssetManager getAssets() { 1531 return mAssets; 1532 } 1533 1534 /** 1535 * Call this to remove all cached loaded layout resources from the 1536 * Resources object. Only intended for use with performance testing 1537 * tools. 1538 */ 1539 public final void flushLayoutCache() { 1540 synchronized (mCachedXmlBlockIds) { 1541 // First see if this block is in our cache. 1542 final int num = mCachedXmlBlockIds.length; 1543 for (int i=0; i<num; i++) { 1544 mCachedXmlBlockIds[i] = -0; 1545 XmlBlock oldBlock = mCachedXmlBlocks[i]; 1546 if (oldBlock != null) { 1547 oldBlock.close(); 1548 } 1549 mCachedXmlBlocks[i] = null; 1550 } 1551 } 1552 } 1553 1554 /** 1555 * Start preloading of resource data using this Resources object. Only 1556 * for use by the zygote process for loading common system resources. 1557 * {@hide} 1558 */ 1559 public final void startPreloading() { 1560 synchronized (mSync) { 1561 if (mPreloaded) { 1562 throw new IllegalStateException("Resources already preloaded"); 1563 } 1564 mPreloaded = true; 1565 mPreloading = true; 1566 } 1567 } 1568 1569 /** 1570 * Called by zygote when it is done preloading resources, to change back 1571 * to normal Resources operation. 1572 */ 1573 public final void finishPreloading() { 1574 if (mPreloading) { 1575 mPreloading = false; 1576 flushLayoutCache(); 1577 } 1578 } 1579 1580 /*package*/ Drawable loadDrawable(TypedValue value, int id) 1581 throws NotFoundException { 1582 1583 if (TRACE_FOR_PRELOAD) { 1584 // Log only framework resources 1585 if ((id >>> 24) == 0x1) { 1586 final String name = getResourceName(id); 1587 if (name != null) android.util.Log.d("PreloadDrawable", name); 1588 } 1589 } 1590 1591 final int key = (value.assetCookie << 24) | value.data; 1592 Drawable dr = getCachedDrawable(key); 1593 1594 if (dr != null) { 1595 return dr; 1596 } 1597 1598 Drawable.ConstantState cs = mPreloadedDrawables.get(key); 1599 if (cs != null) { 1600 dr = cs.newDrawable(); 1601 } else { 1602 if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT && 1603 value.type <= TypedValue.TYPE_LAST_COLOR_INT) { 1604 dr = new ColorDrawable(value.data); 1605 } 1606 1607 if (dr == null) { 1608 if (value.string == null) { 1609 throw new NotFoundException( 1610 "Resource is not a Drawable (color or path): " + value); 1611 } 1612 1613 String file = value.string.toString(); 1614 1615 if (DEBUG_LOAD) Log.v(TAG, "Loading drawable for cookie " 1616 + value.assetCookie + ": " + file); 1617 1618 if (file.endsWith(".xml")) { 1619 try { 1620 XmlResourceParser rp = loadXmlResourceParser( 1621 file, id, value.assetCookie, "drawable"); 1622 dr = Drawable.createFromXml(this, rp); 1623 rp.close(); 1624 } catch (Exception e) { 1625 NotFoundException rnf = new NotFoundException( 1626 "File " + file + " from drawable resource ID #0x" 1627 + Integer.toHexString(id)); 1628 rnf.initCause(e); 1629 throw rnf; 1630 } 1631 1632 } else { 1633 try { 1634 InputStream is = mAssets.openNonAsset( 1635 value.assetCookie, file, AssetManager.ACCESS_BUFFER); 1636 // System.out.println("Opened file " + file + ": " + is); 1637 dr = Drawable.createFromResourceStream(this, value, is, file); 1638 is.close(); 1639 // System.out.println("Created stream: " + dr); 1640 } catch (Exception e) { 1641 NotFoundException rnf = new NotFoundException( 1642 "File " + file + " from drawable resource ID #0x" 1643 + Integer.toHexString(id)); 1644 rnf.initCause(e); 1645 throw rnf; 1646 } 1647 } 1648 } 1649 } 1650 1651 if (dr != null) { 1652 dr.setChangingConfigurations(value.changingConfigurations); 1653 cs = dr.getConstantState(); 1654 if (cs != null) { 1655 if (mPreloading) { 1656 mPreloadedDrawables.put(key, cs); 1657 } else { 1658 synchronized (mTmpValue) { 1659 //Log.i(TAG, "Saving cached drawable @ #" + 1660 // Integer.toHexString(key.intValue()) 1661 // + " in " + this + ": " + cs); 1662 mDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs)); 1663 } 1664 } 1665 } 1666 } 1667 1668 return dr; 1669 } 1670 1671 private Drawable getCachedDrawable(int key) { 1672 synchronized (mTmpValue) { 1673 WeakReference<Drawable.ConstantState> wr = mDrawableCache.get(key); 1674 if (wr != null) { // we have the key 1675 Drawable.ConstantState entry = wr.get(); 1676 if (entry != null) { 1677 //Log.i(TAG, "Returning cached drawable @ #" + 1678 // Integer.toHexString(((Integer)key).intValue()) 1679 // + " in " + this + ": " + entry); 1680 return entry.newDrawable(); 1681 } 1682 else { // our entry has been purged 1683 mDrawableCache.delete(key); 1684 } 1685 } 1686 } 1687 return null; 1688 } 1689 1690 /*package*/ ColorStateList loadColorStateList(TypedValue value, int id) 1691 throws NotFoundException { 1692 if (TRACE_FOR_PRELOAD) { 1693 // Log only framework resources 1694 if ((id >>> 24) == 0x1) { 1695 final String name = getResourceName(id); 1696 if (name != null) android.util.Log.d("PreloadColorStateList", name); 1697 } 1698 } 1699 1700 final int key = (value.assetCookie << 24) | value.data; 1701 1702 ColorStateList csl; 1703 1704 if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT && 1705 value.type <= TypedValue.TYPE_LAST_COLOR_INT) { 1706 1707 csl = mPreloadedColorStateLists.get(key); 1708 if (csl != null) { 1709 return csl; 1710 } 1711 1712 csl = ColorStateList.valueOf(value.data); 1713 if (mPreloading) { 1714 mPreloadedColorStateLists.put(key, csl); 1715 } 1716 1717 return csl; 1718 } 1719 1720 csl = getCachedColorStateList(key); 1721 if (csl != null) { 1722 return csl; 1723 } 1724 1725 csl = mPreloadedColorStateLists.get(key); 1726 if (csl != null) { 1727 return csl; 1728 } 1729 1730 if (value.string == null) { 1731 throw new NotFoundException( 1732 "Resource is not a ColorStateList (color or path): " + value); 1733 } 1734 1735 String file = value.string.toString(); 1736 1737 if (file.endsWith(".xml")) { 1738 try { 1739 XmlResourceParser rp = loadXmlResourceParser( 1740 file, id, value.assetCookie, "colorstatelist"); 1741 csl = ColorStateList.createFromXml(this, rp); 1742 rp.close(); 1743 } catch (Exception e) { 1744 NotFoundException rnf = new NotFoundException( 1745 "File " + file + " from color state list resource ID #0x" 1746 + Integer.toHexString(id)); 1747 rnf.initCause(e); 1748 throw rnf; 1749 } 1750 } else { 1751 throw new NotFoundException( 1752 "File " + file + " from drawable resource ID #0x" 1753 + Integer.toHexString(id) + ": .xml extension required"); 1754 } 1755 1756 if (csl != null) { 1757 if (mPreloading) { 1758 mPreloadedColorStateLists.put(key, csl); 1759 } else { 1760 synchronized (mTmpValue) { 1761 //Log.i(TAG, "Saving cached color state list @ #" + 1762 // Integer.toHexString(key.intValue()) 1763 // + " in " + this + ": " + csl); 1764 mColorStateListCache.put( 1765 key, new WeakReference<ColorStateList>(csl)); 1766 } 1767 } 1768 } 1769 1770 return csl; 1771 } 1772 1773 private ColorStateList getCachedColorStateList(int key) { 1774 synchronized (mTmpValue) { 1775 WeakReference<ColorStateList> wr = mColorStateListCache.get(key); 1776 if (wr != null) { // we have the key 1777 ColorStateList entry = wr.get(); 1778 if (entry != null) { 1779 //Log.i(TAG, "Returning cached color state list @ #" + 1780 // Integer.toHexString(((Integer)key).intValue()) 1781 // + " in " + this + ": " + entry); 1782 return entry; 1783 } 1784 else { // our entry has been purged 1785 mColorStateListCache.delete(key); 1786 } 1787 } 1788 } 1789 return null; 1790 } 1791 1792 /*package*/ XmlResourceParser loadXmlResourceParser(int id, String type) 1793 throws NotFoundException { 1794 synchronized (mTmpValue) { 1795 TypedValue value = mTmpValue; 1796 getValue(id, value, true); 1797 if (value.type == TypedValue.TYPE_STRING) { 1798 return loadXmlResourceParser(value.string.toString(), id, 1799 value.assetCookie, type); 1800 } 1801 throw new NotFoundException( 1802 "Resource ID #0x" + Integer.toHexString(id) + " type #0x" 1803 + Integer.toHexString(value.type) + " is not valid"); 1804 } 1805 } 1806 1807 /*package*/ XmlResourceParser loadXmlResourceParser(String file, int id, 1808 int assetCookie, String type) throws NotFoundException { 1809 if (id != 0) { 1810 try { 1811 // These may be compiled... 1812 synchronized (mCachedXmlBlockIds) { 1813 // First see if this block is in our cache. 1814 final int num = mCachedXmlBlockIds.length; 1815 for (int i=0; i<num; i++) { 1816 if (mCachedXmlBlockIds[i] == id) { 1817 //System.out.println("**** REUSING XML BLOCK! id=" 1818 // + id + ", index=" + i); 1819 return mCachedXmlBlocks[i].newParser(); 1820 } 1821 } 1822 1823 // Not in the cache, create a new block and put it at 1824 // the next slot in the cache. 1825 XmlBlock block = mAssets.openXmlBlockAsset( 1826 assetCookie, file); 1827 if (block != null) { 1828 int pos = mLastCachedXmlBlockIndex+1; 1829 if (pos >= num) pos = 0; 1830 mLastCachedXmlBlockIndex = pos; 1831 XmlBlock oldBlock = mCachedXmlBlocks[pos]; 1832 if (oldBlock != null) { 1833 oldBlock.close(); 1834 } 1835 mCachedXmlBlockIds[pos] = id; 1836 mCachedXmlBlocks[pos] = block; 1837 //System.out.println("**** CACHING NEW XML BLOCK! id=" 1838 // + id + ", index=" + pos); 1839 return block.newParser(); 1840 } 1841 } 1842 } catch (Exception e) { 1843 NotFoundException rnf = new NotFoundException( 1844 "File " + file + " from xml type " + type + " resource ID #0x" 1845 + Integer.toHexString(id)); 1846 rnf.initCause(e); 1847 throw rnf; 1848 } 1849 } 1850 1851 throw new NotFoundException( 1852 "File " + file + " from xml type " + type + " resource ID #0x" 1853 + Integer.toHexString(id)); 1854 } 1855 1856 private TypedArray getCachedStyledAttributes(int len) { 1857 synchronized (mTmpValue) { 1858 TypedArray attrs = mCachedStyledAttributes; 1859 if (attrs != null) { 1860 mCachedStyledAttributes = null; 1861 1862 attrs.mLength = len; 1863 int fullLen = len * AssetManager.STYLE_NUM_ENTRIES; 1864 if (attrs.mData.length >= fullLen) { 1865 return attrs; 1866 } 1867 attrs.mData = new int[fullLen]; 1868 attrs.mIndices = new int[1+len]; 1869 return attrs; 1870 } 1871 return new TypedArray(this, 1872 new int[len*AssetManager.STYLE_NUM_ENTRIES], 1873 new int[1+len], len); 1874 } 1875 } 1876 1877 private Resources() { 1878 mAssets = AssetManager.getSystem(); 1879 // NOTE: Intentionally leaving this uninitialized (all values set 1880 // to zero), so that anyone who tries to do something that requires 1881 // metrics will get a very wrong value. 1882 mConfiguration.setToDefaults(); 1883 mMetrics.setToDefaults(); 1884 updateConfiguration(null, null); 1885 mAssets.ensureStringBlocks(); 1886 } 1887} 1888 1889