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