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