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