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