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