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