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