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