Resources.java revision 7341d7a104b47996445d069a695e155a07184606
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.BitmapFactory; 27import android.graphics.Movie; 28import android.graphics.drawable.Drawable; 29import android.graphics.drawable.ColorDrawable; 30import android.os.Bundle; 31import android.os.SystemProperties; 32import android.util.AttributeSet; 33import android.util.DisplayMetrics; 34import android.util.Log; 35import android.util.SparseArray; 36import android.util.TypedValue; 37import android.util.LongSparseArray; 38import android.view.Display; 39 40import java.io.IOException; 41import java.io.InputStream; 42import java.lang.ref.WeakReference; 43 44/** 45 * Class for accessing an application's resources. This sits on top of the 46 * asset manager of the application (accessible through getAssets()) and 47 * provides a higher-level API for getting typed data from the assets. 48 */ 49public class Resources { 50 static final String TAG = "Resources"; 51 private static final boolean DEBUG_LOAD = false; 52 private static final boolean DEBUG_CONFIG = false; 53 private static final boolean TRACE_FOR_PRELOAD = false; 54 55 private static final int sSdkVersion = SystemProperties.getInt( 56 "ro.build.version.sdk", 0); 57 private static final Object mSync = new Object(); 58 private static Resources mSystem = null; 59 60 // Information about preloaded resources. Note that they are not 61 // protected by a lock, because while preloading in zygote we are all 62 // single-threaded, and after that these are immutable. 63 private static final LongSparseArray<Drawable.ConstantState> sPreloadedDrawables 64 = new LongSparseArray<Drawable.ConstantState>(); 65 private static final SparseArray<ColorStateList> mPreloadedColorStateLists 66 = new SparseArray<ColorStateList>(); 67 private static boolean mPreloaded; 68 69 private final LongSparseArray<Drawable.ConstantState> mPreloadedDrawables; 70 71 /*package*/ final TypedValue mTmpValue = new TypedValue(); 72 73 // These are protected by the mTmpValue lock. 74 private final LongSparseArray<WeakReference<Drawable.ConstantState> > mDrawableCache 75 = new LongSparseArray<WeakReference<Drawable.ConstantState> >(); 76 private final SparseArray<WeakReference<ColorStateList> > mColorStateListCache 77 = new SparseArray<WeakReference<ColorStateList> >(); 78 private boolean mPreloading; 79 80 /*package*/ TypedArray mCachedStyledAttributes = null; 81 82 private int mLastCachedXmlBlockIndex = -1; 83 private final int[] mCachedXmlBlockIds = { 0, 0, 0, 0 }; 84 private final XmlBlock[] mCachedXmlBlocks = new XmlBlock[4]; 85 86 /*package*/ final AssetManager mAssets; 87 private final Configuration mConfiguration = new Configuration(); 88 /*package*/ final DisplayMetrics mMetrics = new DisplayMetrics(); 89 PluralRules mPluralRule; 90 91 private CompatibilityInfo mCompatibilityInfo; 92 private Display mDefaultDisplay; 93 94 private static final LongSparseArray<Object> EMPTY_ARRAY = new LongSparseArray<Object>() { 95 @Override 96 public void put(long k, Object o) { 97 throw new UnsupportedOperationException(); 98 } 99 @Override 100 public void append(long k, Object o) { 101 throw new UnsupportedOperationException(); 102 } 103 }; 104 105 @SuppressWarnings("unchecked") 106 private static <T> LongSparseArray<T> emptySparseArray() { 107 return (LongSparseArray<T>) EMPTY_ARRAY; 108 } 109 110 /** 111 * This exception is thrown by the resource APIs when a requested resource 112 * can not be found. 113 */ 114 public static class NotFoundException extends RuntimeException { 115 public NotFoundException() { 116 } 117 118 public NotFoundException(String name) { 119 super(name); 120 } 121 } 122 123 /** 124 * Create a new Resources object on top of an existing set of assets in an 125 * AssetManager. 126 * 127 * @param assets Previously created AssetManager. 128 * @param metrics Current display metrics to consider when 129 * selecting/computing resource values. 130 * @param config Desired device configuration to consider when 131 * selecting/computing resource values (optional). 132 */ 133 public Resources(AssetManager assets, DisplayMetrics metrics, 134 Configuration config) { 135 this(assets, metrics, config, (CompatibilityInfo) null); 136 } 137 138 /** 139 * Creates a new Resources object with CompatibilityInfo. 140 * 141 * @param assets Previously created AssetManager. 142 * @param metrics Current display metrics to consider when 143 * selecting/computing resource values. 144 * @param config Desired device configuration to consider when 145 * selecting/computing resource values (optional). 146 * @param compInfo this resource's compatibility info. It will use the default compatibility 147 * info when it's null. 148 * @hide 149 */ 150 public Resources(AssetManager assets, DisplayMetrics metrics, 151 Configuration config, CompatibilityInfo compInfo) { 152 mAssets = assets; 153 mMetrics.setToDefaults(); 154 if (compInfo == null) { 155 mCompatibilityInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; 156 } else { 157 mCompatibilityInfo = compInfo; 158 } 159 updateConfiguration(config, metrics); 160 assets.ensureStringBlocks(); 161 if (mCompatibilityInfo.isScalingRequired()) { 162 mPreloadedDrawables = emptySparseArray(); 163 } else { 164 mPreloadedDrawables = sPreloadedDrawables; 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, 1271 mConfiguration.orientation, mConfiguration.screenLayout); 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 mConfiguration.screenLayout, 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 + Long.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 + Long.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 * This is just for testing. 1391 * @hide 1392 */ 1393 public void setCompatibilityInfo(CompatibilityInfo ci) { 1394 mCompatibilityInfo = ci; 1395 updateConfiguration(mConfiguration, mMetrics); 1396 } 1397 1398 /** 1399 * Return a resource identifier for the given resource name. A fully 1400 * qualified resource name is of the form "package:type/entry". The first 1401 * two components (package and type) are optional if defType and 1402 * defPackage, respectively, are specified here. 1403 * 1404 * <p>Note: use of this function is discouraged. It is much more 1405 * efficient to retrieve resources by identifier than by name. 1406 * 1407 * @param name The name of the desired resource. 1408 * @param defType Optional default resource type to find, if "type/" is 1409 * not included in the name. Can be null to require an 1410 * explicit type. 1411 * @param defPackage Optional default package to find, if "package:" is 1412 * not included in the name. Can be null to require an 1413 * explicit package. 1414 * 1415 * @return int The associated resource identifier. Returns 0 if no such 1416 * resource was found. (0 is not a valid resource ID.) 1417 */ 1418 public int getIdentifier(String name, String defType, String defPackage) { 1419 try { 1420 return Integer.parseInt(name); 1421 } catch (Exception e) { 1422 // Ignore 1423 } 1424 return mAssets.getResourceIdentifier(name, defType, defPackage); 1425 } 1426 1427 /** 1428 * Return the full name for a given resource identifier. This name is 1429 * a single string of the form "package:type/entry". 1430 * 1431 * @param resid The resource identifier whose name is to be retrieved. 1432 * 1433 * @return A string holding the name of the resource. 1434 * 1435 * @throws NotFoundException Throws NotFoundException if the given ID does not exist. 1436 * 1437 * @see #getResourcePackageName 1438 * @see #getResourceTypeName 1439 * @see #getResourceEntryName 1440 */ 1441 public String getResourceName(int resid) throws NotFoundException { 1442 String str = mAssets.getResourceName(resid); 1443 if (str != null) return str; 1444 throw new NotFoundException("Unable to find resource ID #0x" 1445 + Integer.toHexString(resid)); 1446 } 1447 1448 /** 1449 * Return the package name for a given resource identifier. 1450 * 1451 * @param resid The resource identifier whose package name is to be 1452 * retrieved. 1453 * 1454 * @return A string holding the package name of the resource. 1455 * 1456 * @throws NotFoundException Throws NotFoundException if the given ID does not exist. 1457 * 1458 * @see #getResourceName 1459 */ 1460 public String getResourcePackageName(int resid) throws NotFoundException { 1461 String str = mAssets.getResourcePackageName(resid); 1462 if (str != null) return str; 1463 throw new NotFoundException("Unable to find resource ID #0x" 1464 + Integer.toHexString(resid)); 1465 } 1466 1467 /** 1468 * Return the type name for a given resource identifier. 1469 * 1470 * @param resid The resource identifier whose type name is to be 1471 * retrieved. 1472 * 1473 * @return A string holding the type name of the resource. 1474 * 1475 * @throws NotFoundException Throws NotFoundException if the given ID does not exist. 1476 * 1477 * @see #getResourceName 1478 */ 1479 public String getResourceTypeName(int resid) throws NotFoundException { 1480 String str = mAssets.getResourceTypeName(resid); 1481 if (str != null) return str; 1482 throw new NotFoundException("Unable to find resource ID #0x" 1483 + Integer.toHexString(resid)); 1484 } 1485 1486 /** 1487 * Return the entry name for a given resource identifier. 1488 * 1489 * @param resid The resource identifier whose entry name is to be 1490 * retrieved. 1491 * 1492 * @return A string holding the entry name of the resource. 1493 * 1494 * @throws NotFoundException Throws NotFoundException if the given ID does not exist. 1495 * 1496 * @see #getResourceName 1497 */ 1498 public String getResourceEntryName(int resid) throws NotFoundException { 1499 String str = mAssets.getResourceEntryName(resid); 1500 if (str != null) return str; 1501 throw new NotFoundException("Unable to find resource ID #0x" 1502 + Integer.toHexString(resid)); 1503 } 1504 1505 /** 1506 * Parse a series of {@link android.R.styleable#Extra <extra>} tags from 1507 * an XML file. You call this when you are at the parent tag of the 1508 * extra tags, and it return once all of the child tags have been parsed. 1509 * This will call {@link #parseBundleExtra} for each extra tag encountered. 1510 * 1511 * @param parser The parser from which to retrieve the extras. 1512 * @param outBundle A Bundle in which to place all parsed extras. 1513 * @throws XmlPullParserException 1514 * @throws IOException 1515 */ 1516 public void parseBundleExtras(XmlResourceParser parser, Bundle outBundle) 1517 throws XmlPullParserException, IOException { 1518 int outerDepth = parser.getDepth(); 1519 int type; 1520 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 1521 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 1522 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 1523 continue; 1524 } 1525 1526 String nodeName = parser.getName(); 1527 if (nodeName.equals("extra")) { 1528 parseBundleExtra("extra", parser, outBundle); 1529 XmlUtils.skipCurrentTag(parser); 1530 1531 } else { 1532 XmlUtils.skipCurrentTag(parser); 1533 } 1534 } 1535 } 1536 1537 /** 1538 * Parse a name/value pair out of an XML tag holding that data. The 1539 * AttributeSet must be holding the data defined by 1540 * {@link android.R.styleable#Extra}. The following value types are supported: 1541 * <ul> 1542 * <li> {@link TypedValue#TYPE_STRING}: 1543 * {@link Bundle#putCharSequence Bundle.putCharSequence()} 1544 * <li> {@link TypedValue#TYPE_INT_BOOLEAN}: 1545 * {@link Bundle#putCharSequence Bundle.putBoolean()} 1546 * <li> {@link TypedValue#TYPE_FIRST_INT}-{@link TypedValue#TYPE_LAST_INT}: 1547 * {@link Bundle#putCharSequence Bundle.putBoolean()} 1548 * <li> {@link TypedValue#TYPE_FLOAT}: 1549 * {@link Bundle#putCharSequence Bundle.putFloat()} 1550 * </ul> 1551 * 1552 * @param tagName The name of the tag these attributes come from; this is 1553 * only used for reporting error messages. 1554 * @param attrs The attributes from which to retrieve the name/value pair. 1555 * @param outBundle The Bundle in which to place the parsed value. 1556 * @throws XmlPullParserException If the attributes are not valid. 1557 */ 1558 public void parseBundleExtra(String tagName, AttributeSet attrs, 1559 Bundle outBundle) throws XmlPullParserException { 1560 TypedArray sa = obtainAttributes(attrs, 1561 com.android.internal.R.styleable.Extra); 1562 1563 String name = sa.getString( 1564 com.android.internal.R.styleable.Extra_name); 1565 if (name == null) { 1566 sa.recycle(); 1567 throw new XmlPullParserException("<" + tagName 1568 + "> requires an android:name attribute at " 1569 + attrs.getPositionDescription()); 1570 } 1571 1572 TypedValue v = sa.peekValue( 1573 com.android.internal.R.styleable.Extra_value); 1574 if (v != null) { 1575 if (v.type == TypedValue.TYPE_STRING) { 1576 CharSequence cs = v.coerceToString(); 1577 outBundle.putCharSequence(name, cs); 1578 } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) { 1579 outBundle.putBoolean(name, v.data != 0); 1580 } else if (v.type >= TypedValue.TYPE_FIRST_INT 1581 && v.type <= TypedValue.TYPE_LAST_INT) { 1582 outBundle.putInt(name, v.data); 1583 } else if (v.type == TypedValue.TYPE_FLOAT) { 1584 outBundle.putFloat(name, v.getFloat()); 1585 } else { 1586 sa.recycle(); 1587 throw new XmlPullParserException("<" + tagName 1588 + "> only supports string, integer, float, color, and boolean at " 1589 + attrs.getPositionDescription()); 1590 } 1591 } else { 1592 sa.recycle(); 1593 throw new XmlPullParserException("<" + tagName 1594 + "> requires an android:value or android:resource attribute at " 1595 + attrs.getPositionDescription()); 1596 } 1597 1598 sa.recycle(); 1599 } 1600 1601 /** 1602 * Retrieve underlying AssetManager storage for these resources. 1603 */ 1604 public final AssetManager getAssets() { 1605 return mAssets; 1606 } 1607 1608 /** 1609 * Call this to remove all cached loaded layout resources from the 1610 * Resources object. Only intended for use with performance testing 1611 * tools. 1612 */ 1613 public final void flushLayoutCache() { 1614 synchronized (mCachedXmlBlockIds) { 1615 // First see if this block is in our cache. 1616 final int num = mCachedXmlBlockIds.length; 1617 for (int i=0; i<num; i++) { 1618 mCachedXmlBlockIds[i] = -0; 1619 XmlBlock oldBlock = mCachedXmlBlocks[i]; 1620 if (oldBlock != null) { 1621 oldBlock.close(); 1622 } 1623 mCachedXmlBlocks[i] = null; 1624 } 1625 } 1626 } 1627 1628 /** 1629 * Start preloading of resource data using this Resources object. Only 1630 * for use by the zygote process for loading common system resources. 1631 * {@hide} 1632 */ 1633 public final void startPreloading() { 1634 synchronized (mSync) { 1635 if (mPreloaded) { 1636 throw new IllegalStateException("Resources already preloaded"); 1637 } 1638 mPreloaded = true; 1639 mPreloading = true; 1640 } 1641 } 1642 1643 /** 1644 * Called by zygote when it is done preloading resources, to change back 1645 * to normal Resources operation. 1646 */ 1647 public final void finishPreloading() { 1648 if (mPreloading) { 1649 mPreloading = false; 1650 flushLayoutCache(); 1651 } 1652 } 1653 1654 /*package*/ Drawable loadDrawable(TypedValue value, int id) 1655 throws NotFoundException { 1656 1657 if (TRACE_FOR_PRELOAD) { 1658 // Log only framework resources 1659 if ((id >>> 24) == 0x1) { 1660 final String name = getResourceName(id); 1661 if (name != null) android.util.Log.d("PreloadDrawable", name); 1662 } 1663 } 1664 1665 final long key = (((long) value.assetCookie) << 32) | value.data; 1666 Drawable dr = getCachedDrawable(key); 1667 1668 if (dr != null) { 1669 return dr; 1670 } 1671 1672 Drawable.ConstantState cs = mPreloadedDrawables.get(key); 1673 if (cs != null) { 1674 dr = cs.newDrawable(); 1675 } else { 1676 if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT && 1677 value.type <= TypedValue.TYPE_LAST_COLOR_INT) { 1678 dr = new ColorDrawable(value.data); 1679 } 1680 1681 if (dr == null) { 1682 if (value.string == null) { 1683 throw new NotFoundException( 1684 "Resource is not a Drawable (color or path): " + value); 1685 } 1686 1687 String file = value.string.toString(); 1688 1689 if (DEBUG_LOAD) Log.v(TAG, "Loading drawable for cookie " 1690 + value.assetCookie + ": " + file); 1691 1692 if (file.endsWith(".xml")) { 1693 try { 1694 XmlResourceParser rp = loadXmlResourceParser( 1695 file, id, value.assetCookie, "drawable"); 1696 dr = Drawable.createFromXml(this, rp); 1697 rp.close(); 1698 } catch (Exception e) { 1699 NotFoundException rnf = new NotFoundException( 1700 "File " + file + " from drawable resource ID #0x" 1701 + Integer.toHexString(id)); 1702 rnf.initCause(e); 1703 throw rnf; 1704 } 1705 1706 } else { 1707 try { 1708 InputStream is = mAssets.openNonAsset( 1709 value.assetCookie, file, AssetManager.ACCESS_BUFFER); 1710 // System.out.println("Opened file " + file + ": " + is); 1711 dr = Drawable.createFromResourceStream(this, value, is, 1712 file, null); 1713 is.close(); 1714 // System.out.println("Created stream: " + dr); 1715 } catch (Exception e) { 1716 NotFoundException rnf = new NotFoundException( 1717 "File " + file + " from drawable resource ID #0x" 1718 + Integer.toHexString(id)); 1719 rnf.initCause(e); 1720 throw rnf; 1721 } 1722 } 1723 } 1724 } 1725 1726 if (dr != null) { 1727 dr.setChangingConfigurations(value.changingConfigurations); 1728 cs = dr.getConstantState(); 1729 if (cs != null) { 1730 if (mPreloading) { 1731 sPreloadedDrawables.put(key, cs); 1732 } else { 1733 synchronized (mTmpValue) { 1734 //Log.i(TAG, "Saving cached drawable @ #" + 1735 // Integer.toHexString(key.intValue()) 1736 // + " in " + this + ": " + cs); 1737 mDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs)); 1738 } 1739 } 1740 } 1741 } 1742 1743 return dr; 1744 } 1745 1746 private Drawable getCachedDrawable(long key) { 1747 synchronized (mTmpValue) { 1748 WeakReference<Drawable.ConstantState> wr = mDrawableCache.get(key); 1749 if (wr != null) { // we have the key 1750 Drawable.ConstantState entry = wr.get(); 1751 if (entry != null) { 1752 //Log.i(TAG, "Returning cached drawable @ #" + 1753 // Integer.toHexString(((Integer)key).intValue()) 1754 // + " in " + this + ": " + entry); 1755 return entry.newDrawable(); 1756 } 1757 else { // our entry has been purged 1758 mDrawableCache.delete(key); 1759 } 1760 } 1761 } 1762 return null; 1763 } 1764 1765 /*package*/ ColorStateList loadColorStateList(TypedValue value, int id) 1766 throws NotFoundException { 1767 if (TRACE_FOR_PRELOAD) { 1768 // Log only framework resources 1769 if ((id >>> 24) == 0x1) { 1770 final String name = getResourceName(id); 1771 if (name != null) android.util.Log.d("PreloadColorStateList", name); 1772 } 1773 } 1774 1775 final int key = (value.assetCookie << 24) | value.data; 1776 1777 ColorStateList csl; 1778 1779 if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT && 1780 value.type <= TypedValue.TYPE_LAST_COLOR_INT) { 1781 1782 csl = mPreloadedColorStateLists.get(key); 1783 if (csl != null) { 1784 return csl; 1785 } 1786 1787 csl = ColorStateList.valueOf(value.data); 1788 if (mPreloading) { 1789 mPreloadedColorStateLists.put(key, csl); 1790 } 1791 1792 return csl; 1793 } 1794 1795 csl = getCachedColorStateList(key); 1796 if (csl != null) { 1797 return csl; 1798 } 1799 1800 csl = mPreloadedColorStateLists.get(key); 1801 if (csl != null) { 1802 return csl; 1803 } 1804 1805 if (value.string == null) { 1806 throw new NotFoundException( 1807 "Resource is not a ColorStateList (color or path): " + value); 1808 } 1809 1810 String file = value.string.toString(); 1811 1812 if (file.endsWith(".xml")) { 1813 try { 1814 XmlResourceParser rp = loadXmlResourceParser( 1815 file, id, value.assetCookie, "colorstatelist"); 1816 csl = ColorStateList.createFromXml(this, rp); 1817 rp.close(); 1818 } catch (Exception e) { 1819 NotFoundException rnf = new NotFoundException( 1820 "File " + file + " from color state list resource ID #0x" 1821 + Integer.toHexString(id)); 1822 rnf.initCause(e); 1823 throw rnf; 1824 } 1825 } else { 1826 throw new NotFoundException( 1827 "File " + file + " from drawable resource ID #0x" 1828 + Integer.toHexString(id) + ": .xml extension required"); 1829 } 1830 1831 if (csl != null) { 1832 if (mPreloading) { 1833 mPreloadedColorStateLists.put(key, csl); 1834 } else { 1835 synchronized (mTmpValue) { 1836 //Log.i(TAG, "Saving cached color state list @ #" + 1837 // Integer.toHexString(key.intValue()) 1838 // + " in " + this + ": " + csl); 1839 mColorStateListCache.put( 1840 key, new WeakReference<ColorStateList>(csl)); 1841 } 1842 } 1843 } 1844 1845 return csl; 1846 } 1847 1848 private ColorStateList getCachedColorStateList(int key) { 1849 synchronized (mTmpValue) { 1850 WeakReference<ColorStateList> wr = mColorStateListCache.get(key); 1851 if (wr != null) { // we have the key 1852 ColorStateList entry = wr.get(); 1853 if (entry != null) { 1854 //Log.i(TAG, "Returning cached color state list @ #" + 1855 // Integer.toHexString(((Integer)key).intValue()) 1856 // + " in " + this + ": " + entry); 1857 return entry; 1858 } 1859 else { // our entry has been purged 1860 mColorStateListCache.delete(key); 1861 } 1862 } 1863 } 1864 return null; 1865 } 1866 1867 /*package*/ XmlResourceParser loadXmlResourceParser(int id, String type) 1868 throws NotFoundException { 1869 synchronized (mTmpValue) { 1870 TypedValue value = mTmpValue; 1871 getValue(id, value, true); 1872 if (value.type == TypedValue.TYPE_STRING) { 1873 return loadXmlResourceParser(value.string.toString(), id, 1874 value.assetCookie, type); 1875 } 1876 throw new NotFoundException( 1877 "Resource ID #0x" + Integer.toHexString(id) + " type #0x" 1878 + Integer.toHexString(value.type) + " is not valid"); 1879 } 1880 } 1881 1882 /*package*/ XmlResourceParser loadXmlResourceParser(String file, int id, 1883 int assetCookie, String type) throws NotFoundException { 1884 if (id != 0) { 1885 try { 1886 // These may be compiled... 1887 synchronized (mCachedXmlBlockIds) { 1888 // First see if this block is in our cache. 1889 final int num = mCachedXmlBlockIds.length; 1890 for (int i=0; i<num; i++) { 1891 if (mCachedXmlBlockIds[i] == id) { 1892 //System.out.println("**** REUSING XML BLOCK! id=" 1893 // + id + ", index=" + i); 1894 return mCachedXmlBlocks[i].newParser(); 1895 } 1896 } 1897 1898 // Not in the cache, create a new block and put it at 1899 // the next slot in the cache. 1900 XmlBlock block = mAssets.openXmlBlockAsset( 1901 assetCookie, file); 1902 if (block != null) { 1903 int pos = mLastCachedXmlBlockIndex+1; 1904 if (pos >= num) pos = 0; 1905 mLastCachedXmlBlockIndex = pos; 1906 XmlBlock oldBlock = mCachedXmlBlocks[pos]; 1907 if (oldBlock != null) { 1908 oldBlock.close(); 1909 } 1910 mCachedXmlBlockIds[pos] = id; 1911 mCachedXmlBlocks[pos] = block; 1912 //System.out.println("**** CACHING NEW XML BLOCK! id=" 1913 // + id + ", index=" + pos); 1914 return block.newParser(); 1915 } 1916 } 1917 } catch (Exception e) { 1918 NotFoundException rnf = new NotFoundException( 1919 "File " + file + " from xml type " + type + " resource ID #0x" 1920 + Integer.toHexString(id)); 1921 rnf.initCause(e); 1922 throw rnf; 1923 } 1924 } 1925 1926 throw new NotFoundException( 1927 "File " + file + " from xml type " + type + " resource ID #0x" 1928 + Integer.toHexString(id)); 1929 } 1930 1931 /** 1932 * Returns the display adjusted for the Resources' metrics. 1933 * @hide 1934 */ 1935 public Display getDefaultDisplay(Display defaultDisplay) { 1936 if (mDefaultDisplay == null) { 1937 if (!mCompatibilityInfo.isScalingRequired() && mCompatibilityInfo.supportsScreen()) { 1938 // the app supports the display. just use the default one. 1939 mDefaultDisplay = defaultDisplay; 1940 } else { 1941 // display needs adjustment. 1942 mDefaultDisplay = Display.createMetricsBasedDisplay( 1943 defaultDisplay.getDisplayId(), mMetrics); 1944 } 1945 } 1946 return mDefaultDisplay; 1947 } 1948 1949 private TypedArray getCachedStyledAttributes(int len) { 1950 synchronized (mTmpValue) { 1951 TypedArray attrs = mCachedStyledAttributes; 1952 if (attrs != null) { 1953 mCachedStyledAttributes = null; 1954 1955 attrs.mLength = len; 1956 int fullLen = len * AssetManager.STYLE_NUM_ENTRIES; 1957 if (attrs.mData.length >= fullLen) { 1958 return attrs; 1959 } 1960 attrs.mData = new int[fullLen]; 1961 attrs.mIndices = new int[1+len]; 1962 return attrs; 1963 } 1964 return new TypedArray(this, 1965 new int[len*AssetManager.STYLE_NUM_ENTRIES], 1966 new int[1+len], len); 1967 } 1968 } 1969 1970 private Resources() { 1971 mAssets = AssetManager.getSystem(); 1972 // NOTE: Intentionally leaving this uninitialized (all values set 1973 // to zero), so that anyone who tries to do something that requires 1974 // metrics will get a very wrong value. 1975 mConfiguration.setToDefaults(); 1976 mMetrics.setToDefaults(); 1977 updateConfiguration(null, null); 1978 mAssets.ensureStringBlocks(); 1979 mPreloadedDrawables = sPreloadedDrawables; 1980 mCompatibilityInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; 1981 } 1982} 1983