1 /* 2 * Copyright (C) 2008 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 17 package android.content.res; 18 19 import android.annotation.AnyRes; 20 import android.annotation.ColorInt; 21 import android.annotation.Nullable; 22 import android.annotation.StyleableRes; 23 import android.compat.annotation.UnsupportedAppUsage; 24 import android.content.pm.ActivityInfo; 25 import android.content.pm.ActivityInfo.Config; 26 import android.graphics.Typeface; 27 import android.graphics.drawable.Drawable; 28 import android.os.Build; 29 import android.os.StrictMode; 30 import android.util.AttributeSet; 31 import android.util.DisplayMetrics; 32 import android.util.TypedValue; 33 34 import com.android.internal.util.XmlUtils; 35 36 import dalvik.system.VMRuntime; 37 38 import java.util.Arrays; 39 40 /** 41 * Container for an array of values that were retrieved with 42 * {@link Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)} 43 * or {@link Resources#obtainAttributes}. Be 44 * sure to call {@link #recycle} when done with them. 45 * 46 * The indices used to retrieve values from this structure correspond to 47 * the positions of the attributes given to obtainStyledAttributes. 48 */ 49 public class TypedArray implements AutoCloseable { 50 obtain(Resources res, int len)51 static TypedArray obtain(Resources res, int len) { 52 TypedArray attrs = res.mTypedArrayPool.acquire(); 53 if (attrs == null) { 54 attrs = new TypedArray(res); 55 } 56 57 attrs.mRecycled = false; 58 // Reset the assets, which may have changed due to configuration changes 59 // or further resource loading. 60 attrs.mAssets = res.getAssets(); 61 attrs.mMetrics = res.getDisplayMetrics(); 62 attrs.resize(len); 63 return attrs; 64 } 65 66 // STYLE_ prefixed constants are offsets within the typed data array. 67 // Keep this in sync with libs/androidfw/include/androidfw/AttributeResolution.h 68 static final int STYLE_NUM_ENTRIES = 7; 69 static final int STYLE_TYPE = 0; 70 static final int STYLE_DATA = 1; 71 static final int STYLE_ASSET_COOKIE = 2; 72 static final int STYLE_RESOURCE_ID = 3; 73 static final int STYLE_CHANGING_CONFIGURATIONS = 4; 74 static final int STYLE_DENSITY = 5; 75 static final int STYLE_SOURCE_RESOURCE_ID = 6; 76 77 @UnsupportedAppUsage 78 private final Resources mResources; 79 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 80 private DisplayMetrics mMetrics; 81 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 82 private AssetManager mAssets; 83 84 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 85 private boolean mRecycled; 86 87 @UnsupportedAppUsage 88 /*package*/ XmlBlock.Parser mXml; 89 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 90 /*package*/ Resources.Theme mTheme; 91 /** 92 * mData is used to hold the value/id and other metadata about each attribute. 93 * 94 * [type, data, asset cookie, resource id, changing configuration, density] 95 * 96 * type - type of this attribute, see TypedValue#TYPE_* 97 * 98 * data - can be used in various ways: 99 * a) actual value of the attribute if type is between #TYPE_FIRST_INT and #TYPE_LAST_INT 100 * 1) color represented by an integer (#TYPE_INT_COLOR_*) 101 * 2) boolean represented by an integer (#TYPE_INT_BOOLEAN) 102 * 3) integer number (#TYPE_TYPE_INT_DEC or #TYPE_INT_HEX) 103 * 4) float number where integer gets interpreted as float (#TYPE_FLOAT, #TYPE_FRACTION 104 * and #TYPE_DIMENSION) 105 * b) index into string block inside AssetManager (#TYPE_STRING) 106 * c) attribute resource id in the current theme/style (#TYPE_ATTRIBUTE) 107 * 108 * asset cookie - used in two ways: 109 * a) for strings, drawables, and fonts it specifies the set of apk assets to look at 110 * (multi-apk case) 111 * b) cookie + asset as a unique identifier for drawable caches 112 * 113 * resource id - id that was finally used to resolve this attribute 114 * 115 * changing configuration - a mask of the configuration parameters for which the values in this 116 * attribute may change 117 * 118 * density - density of drawable pointed to by this attribute 119 */ 120 @UnsupportedAppUsage 121 /*package*/ int[] mData; 122 /** 123 * Pointer to the start of the memory address of mData. It is passed via JNI and used to write 124 * to mData array directly from native code (AttributeResolution.cpp). 125 */ 126 /*package*/ long mDataAddress; 127 @UnsupportedAppUsage 128 /*package*/ int[] mIndices; 129 /** 130 * Similar to mDataAddress, but instead it is a pointer to mIndices address. 131 */ 132 /*package*/ long mIndicesAddress; 133 @UnsupportedAppUsage 134 /*package*/ int mLength; 135 @UnsupportedAppUsage 136 /*package*/ TypedValue mValue = new TypedValue(); 137 resize(int len)138 private void resize(int len) { 139 mLength = len; 140 final int dataLen = len * STYLE_NUM_ENTRIES; 141 final int indicesLen = len + 1; 142 final VMRuntime runtime = VMRuntime.getRuntime(); 143 if (mDataAddress == 0 || mData.length < dataLen) { 144 mData = (int[]) runtime.newNonMovableArray(int.class, dataLen); 145 mDataAddress = runtime.addressOf(mData); 146 mIndices = (int[]) runtime.newNonMovableArray(int.class, indicesLen); 147 mIndicesAddress = runtime.addressOf(mIndices); 148 } 149 } 150 151 /** 152 * Returns the number of values in this array. 153 * 154 * @throws RuntimeException if the TypedArray has already been recycled. 155 */ length()156 public int length() { 157 if (mRecycled) { 158 throw new RuntimeException("Cannot make calls to a recycled instance!"); 159 } 160 161 return mLength; 162 } 163 164 /** 165 * Returns the number of indices in the array that actually have data. Attributes with a value 166 * of @empty are included, as this is an explicit indicator. 167 * 168 * @throws RuntimeException if the TypedArray has already been recycled. 169 */ getIndexCount()170 public int getIndexCount() { 171 if (mRecycled) { 172 throw new RuntimeException("Cannot make calls to a recycled instance!"); 173 } 174 175 return mIndices[0]; 176 } 177 178 /** 179 * Returns an index in the array that has data. Attributes with a value of @empty are included, 180 * as this is an explicit indicator. 181 * 182 * @param at The index you would like to returned, ranging from 0 to 183 * {@link #getIndexCount()}. 184 * 185 * @return The index at the given offset, which can be used with 186 * {@link #getValue} and related APIs. 187 * @throws RuntimeException if the TypedArray has already been recycled. 188 */ getIndex(int at)189 public int getIndex(int at) { 190 if (mRecycled) { 191 throw new RuntimeException("Cannot make calls to a recycled instance!"); 192 } 193 194 return mIndices[1+at]; 195 } 196 197 /** 198 * Returns the Resources object this array was loaded from. 199 * 200 * @throws RuntimeException if the TypedArray has already been recycled. 201 */ getResources()202 public Resources getResources() { 203 if (mRecycled) { 204 throw new RuntimeException("Cannot make calls to a recycled instance!"); 205 } 206 207 return mResources; 208 } 209 210 /** 211 * Retrieves the styled string value for the attribute at <var>index</var>. 212 * <p> 213 * If the attribute is not a string, this method will attempt to coerce 214 * it to a string. 215 * 216 * @param index Index of attribute to retrieve. 217 * 218 * @return CharSequence holding string data. May be styled. Returns 219 * {@code null} if the attribute is not defined or could not be 220 * coerced to a string. 221 * @throws RuntimeException if the TypedArray has already been recycled. 222 */ getText(@tyleableRes int index)223 public CharSequence getText(@StyleableRes int index) { 224 if (mRecycled) { 225 throw new RuntimeException("Cannot make calls to a recycled instance!"); 226 } 227 228 index *= STYLE_NUM_ENTRIES; 229 final int[] data = mData; 230 final int type = data[index + STYLE_TYPE]; 231 if (type == TypedValue.TYPE_NULL) { 232 return null; 233 } else if (type == TypedValue.TYPE_STRING) { 234 return loadStringValueAt(index); 235 } 236 237 final TypedValue v = mValue; 238 if (getValueAt(index, v)) { 239 return v.coerceToString(); 240 } 241 242 // We already checked for TYPE_NULL. This should never happen. 243 throw new RuntimeException("getText of bad type: 0x" + Integer.toHexString(type)); 244 } 245 246 /** 247 * Retrieves the string value for the attribute at <var>index</var>. 248 * <p> 249 * If the attribute is not a string, this method will attempt to coerce 250 * it to a string. 251 * 252 * @param index Index of attribute to retrieve. 253 * 254 * @return String holding string data. Any styling information is removed. 255 * Returns {@code null} if the attribute is not defined or could 256 * not be coerced to a string. 257 * @throws RuntimeException if the TypedArray has already been recycled. 258 */ 259 @Nullable getString(@tyleableRes int index)260 public String getString(@StyleableRes int index) { 261 if (mRecycled) { 262 throw new RuntimeException("Cannot make calls to a recycled instance!"); 263 } 264 265 index *= STYLE_NUM_ENTRIES; 266 final int[] data = mData; 267 final int type = data[index + STYLE_TYPE]; 268 if (type == TypedValue.TYPE_NULL) { 269 return null; 270 } else if (type == TypedValue.TYPE_STRING) { 271 return loadStringValueAt(index).toString(); 272 } 273 274 final TypedValue v = mValue; 275 if (getValueAt(index, v)) { 276 final CharSequence cs = v.coerceToString(); 277 return cs != null ? cs.toString() : null; 278 } 279 280 // We already checked for TYPE_NULL. This should never happen. 281 throw new RuntimeException("getString of bad type: 0x" + Integer.toHexString(type)); 282 } 283 284 /** 285 * Retrieves the string value for the attribute at <var>index</var>, but 286 * only if that string comes from an immediate value in an XML file. That 287 * is, this does not allow references to string resources, string 288 * attributes, or conversions from other types. As such, this method 289 * will only return strings for TypedArray objects that come from 290 * attributes in an XML file. 291 * 292 * @param index Index of attribute to retrieve. 293 * 294 * @return String holding string data. Any styling information is removed. 295 * Returns {@code null} if the attribute is not defined or is not 296 * an immediate string value. 297 * @throws RuntimeException if the TypedArray has already been recycled. 298 */ getNonResourceString(@tyleableRes int index)299 public String getNonResourceString(@StyleableRes int index) { 300 if (mRecycled) { 301 throw new RuntimeException("Cannot make calls to a recycled instance!"); 302 } 303 304 index *= STYLE_NUM_ENTRIES; 305 final int[] data = mData; 306 final int type = data[index + STYLE_TYPE]; 307 if (type == TypedValue.TYPE_STRING) { 308 final int cookie = data[index + STYLE_ASSET_COOKIE]; 309 if (cookie < 0) { 310 return mXml.getPooledString(data[index + STYLE_DATA]).toString(); 311 } 312 } 313 return null; 314 } 315 316 /** 317 * Retrieves the string value for the attribute at <var>index</var> that is 318 * not allowed to change with the given configurations. 319 * 320 * @param index Index of attribute to retrieve. 321 * @param allowedChangingConfigs Bit mask of configurations from 322 * {@link Configuration}.NATIVE_CONFIG_* that are allowed to change. 323 * 324 * @return String holding string data. Any styling information is removed. 325 * Returns {@code null} if the attribute is not defined. 326 * @throws RuntimeException if the TypedArray has already been recycled. 327 * @hide 328 */ 329 @UnsupportedAppUsage getNonConfigurationString(@tyleableRes int index, @Config int allowedChangingConfigs)330 public String getNonConfigurationString(@StyleableRes int index, 331 @Config int allowedChangingConfigs) { 332 if (mRecycled) { 333 throw new RuntimeException("Cannot make calls to a recycled instance!"); 334 } 335 336 index *= STYLE_NUM_ENTRIES; 337 final int[] data = mData; 338 final int type = data[index + STYLE_TYPE]; 339 final @Config int changingConfigs = ActivityInfo.activityInfoConfigNativeToJava( 340 data[index + STYLE_CHANGING_CONFIGURATIONS]); 341 if ((changingConfigs & ~allowedChangingConfigs) != 0) { 342 return null; 343 } 344 if (type == TypedValue.TYPE_NULL) { 345 return null; 346 } else if (type == TypedValue.TYPE_STRING) { 347 return loadStringValueAt(index).toString(); 348 } 349 350 final TypedValue v = mValue; 351 if (getValueAt(index, v)) { 352 final CharSequence cs = v.coerceToString(); 353 return cs != null ? cs.toString() : null; 354 } 355 356 // We already checked for TYPE_NULL. This should never happen. 357 throw new RuntimeException("getNonConfigurationString of bad type: 0x" 358 + Integer.toHexString(type)); 359 } 360 361 /** 362 * Retrieve the boolean value for the attribute at <var>index</var>. 363 * <p> 364 * If the attribute is an integer value, this method returns false if the 365 * attribute is equal to zero, and true otherwise. 366 * If the attribute is not a boolean or integer value, 367 * this method will attempt to coerce it to an integer using 368 * {@link Integer#decode(String)} and return whether it is equal to zero. 369 * 370 * @param index Index of attribute to retrieve. 371 * @param defValue Value to return if the attribute is not defined or 372 * cannot be coerced to an integer. 373 * 374 * @return Boolean value of the attribute, or defValue if the attribute was 375 * not defined or could not be coerced to an integer. 376 * @throws RuntimeException if the TypedArray has already been recycled. 377 */ getBoolean(@tyleableRes int index, boolean defValue)378 public boolean getBoolean(@StyleableRes int index, boolean defValue) { 379 if (mRecycled) { 380 throw new RuntimeException("Cannot make calls to a recycled instance!"); 381 } 382 383 index *= STYLE_NUM_ENTRIES; 384 final int[] data = mData; 385 final int type = data[index + STYLE_TYPE]; 386 if (type == TypedValue.TYPE_NULL) { 387 return defValue; 388 } else if (type >= TypedValue.TYPE_FIRST_INT 389 && type <= TypedValue.TYPE_LAST_INT) { 390 return data[index + STYLE_DATA] != 0; 391 } 392 393 final TypedValue v = mValue; 394 if (getValueAt(index, v)) { 395 StrictMode.noteResourceMismatch(v); 396 return XmlUtils.convertValueToBoolean(v.coerceToString(), defValue); 397 } 398 399 // We already checked for TYPE_NULL. This should never happen. 400 throw new RuntimeException("getBoolean of bad type: 0x" + Integer.toHexString(type)); 401 } 402 403 /** 404 * Retrieve the integer value for the attribute at <var>index</var>. 405 * <p> 406 * If the attribute is not an integer, this method will attempt to coerce 407 * it to an integer using {@link Integer#decode(String)}. 408 * 409 * @param index Index of attribute to retrieve. 410 * @param defValue Value to return if the attribute is not defined or 411 * cannot be coerced to an integer. 412 * 413 * @return Integer value of the attribute, or defValue if the attribute was 414 * not defined or could not be coerced to an integer. 415 * @throws RuntimeException if the TypedArray has already been recycled. 416 */ getInt(@tyleableRes int index, int defValue)417 public int getInt(@StyleableRes int index, int defValue) { 418 if (mRecycled) { 419 throw new RuntimeException("Cannot make calls to a recycled instance!"); 420 } 421 422 index *= STYLE_NUM_ENTRIES; 423 final int[] data = mData; 424 final int type = data[index + STYLE_TYPE]; 425 if (type == TypedValue.TYPE_NULL) { 426 return defValue; 427 } else if (type >= TypedValue.TYPE_FIRST_INT 428 && type <= TypedValue.TYPE_LAST_INT) { 429 return data[index + STYLE_DATA]; 430 } 431 432 final TypedValue v = mValue; 433 if (getValueAt(index, v)) { 434 StrictMode.noteResourceMismatch(v); 435 return XmlUtils.convertValueToInt(v.coerceToString(), defValue); 436 } 437 438 // We already checked for TYPE_NULL. This should never happen. 439 throw new RuntimeException("getInt of bad type: 0x" + Integer.toHexString(type)); 440 } 441 442 /** 443 * Retrieve the float value for the attribute at <var>index</var>. 444 * <p> 445 * If the attribute is not a float or an integer, this method will attempt 446 * to coerce it to a float using {@link Float#parseFloat(String)}. 447 * 448 * @param index Index of attribute to retrieve. 449 * 450 * @return Attribute float value, or defValue if the attribute was 451 * not defined or could not be coerced to a float. 452 * @throws RuntimeException if the TypedArray has already been recycled. 453 */ getFloat(@tyleableRes int index, float defValue)454 public float getFloat(@StyleableRes int index, float defValue) { 455 if (mRecycled) { 456 throw new RuntimeException("Cannot make calls to a recycled instance!"); 457 } 458 459 index *= STYLE_NUM_ENTRIES; 460 final int[] data = mData; 461 final int type = data[index + STYLE_TYPE]; 462 if (type == TypedValue.TYPE_NULL) { 463 return defValue; 464 } else if (type == TypedValue.TYPE_FLOAT) { 465 return Float.intBitsToFloat(data[index + STYLE_DATA]); 466 } else if (type >= TypedValue.TYPE_FIRST_INT 467 && type <= TypedValue.TYPE_LAST_INT) { 468 return data[index + STYLE_DATA]; 469 } 470 471 final TypedValue v = mValue; 472 if (getValueAt(index, v)) { 473 final CharSequence str = v.coerceToString(); 474 if (str != null) { 475 StrictMode.noteResourceMismatch(v); 476 return Float.parseFloat(str.toString()); 477 } 478 } 479 480 // We already checked for TYPE_NULL. This should never happen. 481 throw new RuntimeException("getFloat of bad type: 0x" + Integer.toHexString(type)); 482 } 483 484 /** 485 * Retrieve the color value for the attribute at <var>index</var>. If 486 * the attribute references a color resource holding a complex 487 * {@link android.content.res.ColorStateList}, then the default color from 488 * the set is returned. 489 * <p> 490 * This method will throw an exception if the attribute is defined but is 491 * not an integer color or color state list. 492 * 493 * @param index Index of attribute to retrieve. 494 * @param defValue Value to return if the attribute is not defined or 495 * not a resource. 496 * 497 * @return Attribute color value, or defValue if not defined. 498 * @throws RuntimeException if the TypedArray has already been recycled. 499 * @throws UnsupportedOperationException if the attribute is defined but is 500 * not an integer color or color state list. 501 */ 502 @ColorInt getColor(@tyleableRes int index, @ColorInt int defValue)503 public int getColor(@StyleableRes int index, @ColorInt int defValue) { 504 if (mRecycled) { 505 throw new RuntimeException("Cannot make calls to a recycled instance!"); 506 } 507 508 final int attrIndex = index; 509 index *= STYLE_NUM_ENTRIES; 510 511 final int[] data = mData; 512 final int type = data[index + STYLE_TYPE]; 513 if (type == TypedValue.TYPE_NULL) { 514 return defValue; 515 } else if (type >= TypedValue.TYPE_FIRST_INT 516 && type <= TypedValue.TYPE_LAST_INT) { 517 return data[index + STYLE_DATA]; 518 } else if (type == TypedValue.TYPE_STRING) { 519 final TypedValue value = mValue; 520 if (getValueAt(index, value)) { 521 final ColorStateList csl = mResources.loadColorStateList( 522 value, value.resourceId, mTheme); 523 return csl.getDefaultColor(); 524 } 525 return defValue; 526 } else if (type == TypedValue.TYPE_ATTRIBUTE) { 527 final TypedValue value = mValue; 528 getValueAt(index, value); 529 throw new UnsupportedOperationException( 530 "Failed to resolve attribute at index " + attrIndex + ": " + value); 531 } 532 533 throw new UnsupportedOperationException("Can't convert value at index " + attrIndex 534 + " to color: type=0x" + Integer.toHexString(type)); 535 } 536 537 /** 538 * Retrieve the ComplexColor for the attribute at <var>index</var>. 539 * The value may be either a {@link android.content.res.ColorStateList} which can wrap a simple 540 * color value or a {@link android.content.res.GradientColor} 541 * <p> 542 * This method will return {@code null} if the attribute is not defined or 543 * is not an integer color, color state list or GradientColor. 544 * 545 * @param index Index of attribute to retrieve. 546 * 547 * @return ComplexColor for the attribute, or {@code null} if not defined. 548 * @throws RuntimeException if the attribute if the TypedArray has already 549 * been recycled. 550 * @throws UnsupportedOperationException if the attribute is defined but is 551 * not an integer color, color state list or GradientColor. 552 * @hide 553 */ 554 @Nullable getComplexColor(@tyleableRes int index)555 public ComplexColor getComplexColor(@StyleableRes int index) { 556 if (mRecycled) { 557 throw new RuntimeException("Cannot make calls to a recycled instance!"); 558 } 559 560 final TypedValue value = mValue; 561 if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { 562 if (value.type == TypedValue.TYPE_ATTRIBUTE) { 563 throw new UnsupportedOperationException( 564 "Failed to resolve attribute at index " + index + ": " + value); 565 } 566 return mResources.loadComplexColor(value, value.resourceId, mTheme); 567 } 568 return null; 569 } 570 571 /** 572 * Retrieve the ColorStateList for the attribute at <var>index</var>. 573 * The value may be either a single solid color or a reference to 574 * a color or complex {@link android.content.res.ColorStateList} 575 * description. 576 * <p> 577 * This method will return {@code null} if the attribute is not defined or 578 * is not an integer color or color state list. 579 * 580 * @param index Index of attribute to retrieve. 581 * 582 * @return ColorStateList for the attribute, or {@code null} if not 583 * defined. 584 * @throws RuntimeException if the attribute if the TypedArray has already 585 * been recycled. 586 * @throws UnsupportedOperationException if the attribute is defined but is 587 * not an integer color or color state list. 588 */ 589 @Nullable getColorStateList(@tyleableRes int index)590 public ColorStateList getColorStateList(@StyleableRes int index) { 591 if (mRecycled) { 592 throw new RuntimeException("Cannot make calls to a recycled instance!"); 593 } 594 595 final TypedValue value = mValue; 596 if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { 597 if (value.type == TypedValue.TYPE_ATTRIBUTE) { 598 throw new UnsupportedOperationException( 599 "Failed to resolve attribute at index " + index + ": " + value); 600 } 601 return mResources.loadColorStateList(value, value.resourceId, mTheme); 602 } 603 return null; 604 } 605 606 /** 607 * Retrieve the integer value for the attribute at <var>index</var>. 608 * <p> 609 * Unlike {@link #getInt(int, int)}, this method will throw an exception if 610 * the attribute is defined but is not an integer. 611 * 612 * @param index Index of attribute to retrieve. 613 * @param defValue Value to return if the attribute is not defined or 614 * not a resource. 615 * 616 * @return Attribute integer value, or defValue if not defined. 617 * @throws RuntimeException if the TypedArray has already been recycled. 618 * @throws UnsupportedOperationException if the attribute is defined but is 619 * not an integer. 620 */ getInteger(@tyleableRes int index, int defValue)621 public int getInteger(@StyleableRes int index, int defValue) { 622 if (mRecycled) { 623 throw new RuntimeException("Cannot make calls to a recycled instance!"); 624 } 625 626 final int attrIndex = index; 627 index *= STYLE_NUM_ENTRIES; 628 629 final int[] data = mData; 630 final int type = data[index + STYLE_TYPE]; 631 if (type == TypedValue.TYPE_NULL) { 632 return defValue; 633 } else if (type >= TypedValue.TYPE_FIRST_INT 634 && type <= TypedValue.TYPE_LAST_INT) { 635 return data[index + STYLE_DATA]; 636 } else if (type == TypedValue.TYPE_ATTRIBUTE) { 637 final TypedValue value = mValue; 638 getValueAt(index, value); 639 throw new UnsupportedOperationException( 640 "Failed to resolve attribute at index " + attrIndex + ": " + value); 641 } 642 643 throw new UnsupportedOperationException("Can't convert value at index " + attrIndex 644 + " to integer: type=0x" + Integer.toHexString(type)); 645 } 646 647 /** 648 * Retrieve a dimensional unit attribute at <var>index</var>. Unit 649 * conversions are based on the current {@link DisplayMetrics} 650 * associated with the resources this {@link TypedArray} object 651 * came from. 652 * <p> 653 * This method will throw an exception if the attribute is defined but is 654 * not a dimension. 655 * 656 * @param index Index of attribute to retrieve. 657 * @param defValue Value to return if the attribute is not defined or 658 * not a resource. 659 * 660 * @return Attribute dimension value multiplied by the appropriate 661 * metric, or defValue if not defined. 662 * @throws RuntimeException if the TypedArray has already been recycled. 663 * @throws UnsupportedOperationException if the attribute is defined but is 664 * not an integer. 665 * 666 * @see #getDimensionPixelOffset 667 * @see #getDimensionPixelSize 668 */ getDimension(@tyleableRes int index, float defValue)669 public float getDimension(@StyleableRes int index, float defValue) { 670 if (mRecycled) { 671 throw new RuntimeException("Cannot make calls to a recycled instance!"); 672 } 673 674 final int attrIndex = index; 675 index *= STYLE_NUM_ENTRIES; 676 677 final int[] data = mData; 678 final int type = data[index + STYLE_TYPE]; 679 if (type == TypedValue.TYPE_NULL) { 680 return defValue; 681 } else if (type == TypedValue.TYPE_DIMENSION) { 682 return TypedValue.complexToDimension(data[index + STYLE_DATA], mMetrics); 683 } else if (type == TypedValue.TYPE_ATTRIBUTE) { 684 final TypedValue value = mValue; 685 getValueAt(index, value); 686 throw new UnsupportedOperationException( 687 "Failed to resolve attribute at index " + attrIndex + ": " + value); 688 } 689 690 throw new UnsupportedOperationException("Can't convert value at index " + attrIndex 691 + " to dimension: type=0x" + Integer.toHexString(type)); 692 } 693 694 /** 695 * Retrieve a dimensional unit attribute at <var>index</var> for use 696 * as an offset in raw pixels. This is the same as 697 * {@link #getDimension}, except the returned value is converted to 698 * integer pixels for you. An offset conversion involves simply 699 * truncating the base value to an integer. 700 * <p> 701 * This method will throw an exception if the attribute is defined but is 702 * not a dimension. 703 * 704 * @param index Index of attribute to retrieve. 705 * @param defValue Value to return if the attribute is not defined or 706 * not a resource. 707 * 708 * @return Attribute dimension value multiplied by the appropriate 709 * metric and truncated to integer pixels, or defValue if not defined. 710 * @throws RuntimeException if the TypedArray has already been recycled. 711 * @throws UnsupportedOperationException if the attribute is defined but is 712 * not an integer. 713 * 714 * @see #getDimension 715 * @see #getDimensionPixelSize 716 */ getDimensionPixelOffset(@tyleableRes int index, int defValue)717 public int getDimensionPixelOffset(@StyleableRes int index, int defValue) { 718 if (mRecycled) { 719 throw new RuntimeException("Cannot make calls to a recycled instance!"); 720 } 721 722 final int attrIndex = index; 723 index *= STYLE_NUM_ENTRIES; 724 725 final int[] data = mData; 726 final int type = data[index + STYLE_TYPE]; 727 if (type == TypedValue.TYPE_NULL) { 728 return defValue; 729 } else if (type == TypedValue.TYPE_DIMENSION) { 730 return TypedValue.complexToDimensionPixelOffset(data[index + STYLE_DATA], mMetrics); 731 } else if (type == TypedValue.TYPE_ATTRIBUTE) { 732 final TypedValue value = mValue; 733 getValueAt(index, value); 734 throw new UnsupportedOperationException( 735 "Failed to resolve attribute at index " + attrIndex + ": " + value); 736 } 737 738 throw new UnsupportedOperationException("Can't convert value at index " + attrIndex 739 + " to dimension: type=0x" + Integer.toHexString(type)); 740 } 741 742 /** 743 * Retrieve a dimensional unit attribute at <var>index</var> for use 744 * as a size in raw pixels. This is the same as 745 * {@link #getDimension}, except the returned value is converted to 746 * integer pixels for use as a size. A size conversion involves 747 * rounding the base value, and ensuring that a non-zero base value 748 * is at least one pixel in size. 749 * <p> 750 * This method will throw an exception if the attribute is defined but is 751 * not a dimension. 752 * 753 * @param index Index of attribute to retrieve. 754 * @param defValue Value to return if the attribute is not defined or 755 * not a resource. 756 * 757 * @return Attribute dimension value multiplied by the appropriate 758 * metric and truncated to integer pixels, or defValue if not defined. 759 * @throws RuntimeException if the TypedArray has already been recycled. 760 * @throws UnsupportedOperationException if the attribute is defined but is 761 * not a dimension. 762 * 763 * @see #getDimension 764 * @see #getDimensionPixelOffset 765 */ getDimensionPixelSize(@tyleableRes int index, int defValue)766 public int getDimensionPixelSize(@StyleableRes int index, int defValue) { 767 if (mRecycled) { 768 throw new RuntimeException("Cannot make calls to a recycled instance!"); 769 } 770 771 final int attrIndex = index; 772 index *= STYLE_NUM_ENTRIES; 773 774 final int[] data = mData; 775 final int type = data[index + STYLE_TYPE]; 776 if (type == TypedValue.TYPE_NULL) { 777 return defValue; 778 } else if (type == TypedValue.TYPE_DIMENSION) { 779 return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); 780 } else if (type == TypedValue.TYPE_ATTRIBUTE) { 781 final TypedValue value = mValue; 782 getValueAt(index, value); 783 throw new UnsupportedOperationException( 784 "Failed to resolve attribute at index " + attrIndex + ": " + value); 785 } 786 787 throw new UnsupportedOperationException("Can't convert value at index " + attrIndex 788 + " to dimension: type=0x" + Integer.toHexString(type)); 789 } 790 791 /** 792 * Special version of {@link #getDimensionPixelSize} for retrieving 793 * {@link android.view.ViewGroup}'s layout_width and layout_height 794 * attributes. This is only here for performance reasons; applications 795 * should use {@link #getDimensionPixelSize}. 796 * <p> 797 * This method will throw an exception if the attribute is defined but is 798 * not a dimension or integer (enum). 799 * 800 * @param index Index of the attribute to retrieve. 801 * @param name Textual name of attribute for error reporting. 802 * 803 * @return Attribute dimension value multiplied by the appropriate 804 * metric and truncated to integer pixels. 805 * @throws RuntimeException if the TypedArray has already been recycled. 806 * @throws UnsupportedOperationException if the attribute is defined but is 807 * not a dimension or integer (enum). 808 */ getLayoutDimension(@tyleableRes int index, String name)809 public int getLayoutDimension(@StyleableRes int index, String name) { 810 if (mRecycled) { 811 throw new RuntimeException("Cannot make calls to a recycled instance!"); 812 } 813 814 final int attrIndex = index; 815 index *= STYLE_NUM_ENTRIES; 816 817 final int[] data = mData; 818 final int type = data[index + STYLE_TYPE]; 819 if (type >= TypedValue.TYPE_FIRST_INT 820 && type <= TypedValue.TYPE_LAST_INT) { 821 return data[index + STYLE_DATA]; 822 } else if (type == TypedValue.TYPE_DIMENSION) { 823 return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); 824 } else if (type == TypedValue.TYPE_ATTRIBUTE) { 825 final TypedValue value = mValue; 826 getValueAt(index, value); 827 throw new UnsupportedOperationException( 828 "Failed to resolve attribute at index " + attrIndex + ": " + value); 829 } 830 831 throw new UnsupportedOperationException(getPositionDescription() 832 + ": You must supply a " + name + " attribute."); 833 } 834 835 /** 836 * Special version of {@link #getDimensionPixelSize} for retrieving 837 * {@link android.view.ViewGroup}'s layout_width and layout_height 838 * attributes. This is only here for performance reasons; applications 839 * should use {@link #getDimensionPixelSize}. 840 * 841 * @param index Index of the attribute to retrieve. 842 * @param defValue The default value to return if this attribute is not 843 * default or contains the wrong type of data. 844 * 845 * @return Attribute dimension value multiplied by the appropriate 846 * metric and truncated to integer pixels. 847 * @throws RuntimeException if the TypedArray has already been recycled. 848 */ getLayoutDimension(@tyleableRes int index, int defValue)849 public int getLayoutDimension(@StyleableRes int index, int defValue) { 850 if (mRecycled) { 851 throw new RuntimeException("Cannot make calls to a recycled instance!"); 852 } 853 854 index *= STYLE_NUM_ENTRIES; 855 final int[] data = mData; 856 final int type = data[index + STYLE_TYPE]; 857 if (type >= TypedValue.TYPE_FIRST_INT 858 && type <= TypedValue.TYPE_LAST_INT) { 859 return data[index + STYLE_DATA]; 860 } else if (type == TypedValue.TYPE_DIMENSION) { 861 return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); 862 } 863 864 return defValue; 865 } 866 867 /** 868 * Retrieves a fractional unit attribute at <var>index</var>. 869 * 870 * @param index Index of attribute to retrieve. 871 * @param base The base value of this fraction. In other words, a 872 * standard fraction is multiplied by this value. 873 * @param pbase The parent base value of this fraction. In other 874 * words, a parent fraction (nn%p) is multiplied by this 875 * value. 876 * @param defValue Value to return if the attribute is not defined or 877 * not a resource. 878 * 879 * @return Attribute fractional value multiplied by the appropriate 880 * base value, or defValue if not defined. 881 * @throws RuntimeException if the TypedArray has already been recycled. 882 * @throws UnsupportedOperationException if the attribute is defined but is 883 * not a fraction. 884 */ getFraction(@tyleableRes int index, int base, int pbase, float defValue)885 public float getFraction(@StyleableRes int index, int base, int pbase, float defValue) { 886 if (mRecycled) { 887 throw new RuntimeException("Cannot make calls to a recycled instance!"); 888 } 889 890 final int attrIndex = index; 891 index *= STYLE_NUM_ENTRIES; 892 893 final int[] data = mData; 894 final int type = data[index + STYLE_TYPE]; 895 if (type == TypedValue.TYPE_NULL) { 896 return defValue; 897 } else if (type == TypedValue.TYPE_FRACTION) { 898 return TypedValue.complexToFraction(data[index + STYLE_DATA], base, pbase); 899 } else if (type == TypedValue.TYPE_ATTRIBUTE) { 900 final TypedValue value = mValue; 901 getValueAt(index, value); 902 throw new UnsupportedOperationException( 903 "Failed to resolve attribute at index " + attrIndex + ": " + value); 904 } 905 906 throw new UnsupportedOperationException("Can't convert value at index " + attrIndex 907 + " to fraction: type=0x" + Integer.toHexString(type)); 908 } 909 910 /** 911 * Retrieves the resource identifier for the attribute at 912 * <var>index</var>. Note that attribute resource as resolved when 913 * the overall {@link TypedArray} object is retrieved. As a 914 * result, this function will return the resource identifier of the 915 * final resource value that was found, <em>not</em> necessarily the 916 * original resource that was specified by the attribute. 917 * 918 * @param index Index of attribute to retrieve. 919 * @param defValue Value to return if the attribute is not defined or 920 * not a resource. 921 * 922 * @return Attribute resource identifier, or defValue if not defined. 923 * @throws RuntimeException if the TypedArray has already been recycled. 924 */ 925 @AnyRes getResourceId(@tyleableRes int index, int defValue)926 public int getResourceId(@StyleableRes int index, int defValue) { 927 if (mRecycled) { 928 throw new RuntimeException("Cannot make calls to a recycled instance!"); 929 } 930 931 index *= STYLE_NUM_ENTRIES; 932 final int[] data = mData; 933 if (data[index + STYLE_TYPE] != TypedValue.TYPE_NULL) { 934 final int resid = data[index + STYLE_RESOURCE_ID]; 935 if (resid != 0) { 936 return resid; 937 } 938 } 939 return defValue; 940 } 941 942 /** 943 * Retrieves the theme attribute resource identifier for the attribute at 944 * <var>index</var>. 945 * 946 * @param index Index of attribute to retrieve. 947 * @param defValue Value to return if the attribute is not defined or not a 948 * resource. 949 * 950 * @return Theme attribute resource identifier, or defValue if not defined. 951 * @throws RuntimeException if the TypedArray has already been recycled. 952 * @hide 953 */ getThemeAttributeId(@tyleableRes int index, int defValue)954 public int getThemeAttributeId(@StyleableRes int index, int defValue) { 955 if (mRecycled) { 956 throw new RuntimeException("Cannot make calls to a recycled instance!"); 957 } 958 959 index *= STYLE_NUM_ENTRIES; 960 final int[] data = mData; 961 if (data[index + STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) { 962 return data[index + STYLE_DATA]; 963 } 964 return defValue; 965 } 966 967 /** 968 * Retrieve the Drawable for the attribute at <var>index</var>. 969 * <p> 970 * This method will throw an exception if the attribute is defined but is 971 * not a color or drawable resource. 972 * 973 * @param index Index of attribute to retrieve. 974 * 975 * @return Drawable for the attribute, or {@code null} if not defined. 976 * @throws RuntimeException if the TypedArray has already been recycled. 977 * @throws UnsupportedOperationException if the attribute is defined but is 978 * not a color or drawable resource. 979 */ 980 @Nullable getDrawable(@tyleableRes int index)981 public Drawable getDrawable(@StyleableRes int index) { 982 return getDrawableForDensity(index, 0); 983 } 984 985 /** 986 * Version of {@link #getDrawable(int)} that accepts an override density. 987 * @hide 988 */ 989 @Nullable getDrawableForDensity(@tyleableRes int index, int density)990 public Drawable getDrawableForDensity(@StyleableRes int index, int density) { 991 if (mRecycled) { 992 throw new RuntimeException("Cannot make calls to a recycled instance!"); 993 } 994 995 final TypedValue value = mValue; 996 if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { 997 if (value.type == TypedValue.TYPE_ATTRIBUTE) { 998 throw new UnsupportedOperationException( 999 "Failed to resolve attribute at index " + index + ": " + value); 1000 } 1001 1002 if (density > 0) { 1003 // If the density is overridden, the value in the TypedArray will not reflect this. 1004 // Do a separate lookup of the resourceId with the density override. 1005 mResources.getValueForDensity(value.resourceId, density, value, true); 1006 } 1007 return mResources.loadDrawable(value, value.resourceId, density, mTheme); 1008 } 1009 return null; 1010 } 1011 1012 /** 1013 * Retrieve the Typeface for the attribute at <var>index</var>. 1014 * <p> 1015 * This method will throw an exception if the attribute is defined but is 1016 * not a font. 1017 * 1018 * @param index Index of attribute to retrieve. 1019 * 1020 * @return Typeface for the attribute, or {@code null} if not defined. 1021 * @throws RuntimeException if the TypedArray has already been recycled. 1022 * @throws UnsupportedOperationException if the attribute is defined but is 1023 * not a font resource. 1024 */ 1025 @Nullable getFont(@tyleableRes int index)1026 public Typeface getFont(@StyleableRes int index) { 1027 if (mRecycled) { 1028 throw new RuntimeException("Cannot make calls to a recycled instance!"); 1029 } 1030 1031 final TypedValue value = mValue; 1032 if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { 1033 if (value.type == TypedValue.TYPE_ATTRIBUTE) { 1034 throw new UnsupportedOperationException( 1035 "Failed to resolve attribute at index " + index + ": " + value); 1036 } 1037 return mResources.getFont(value, value.resourceId); 1038 } 1039 return null; 1040 } 1041 1042 /** 1043 * Retrieve the CharSequence[] for the attribute at <var>index</var>. 1044 * This gets the resource ID of the selected attribute, and uses 1045 * {@link Resources#getTextArray Resources.getTextArray} of the owning 1046 * Resources object to retrieve its String[]. 1047 * <p> 1048 * This method will throw an exception if the attribute is defined but is 1049 * not a text array resource. 1050 * 1051 * @param index Index of attribute to retrieve. 1052 * 1053 * @return CharSequence[] for the attribute, or {@code null} if not 1054 * defined. 1055 * @throws RuntimeException if the TypedArray has already been recycled. 1056 */ getTextArray(@tyleableRes int index)1057 public CharSequence[] getTextArray(@StyleableRes int index) { 1058 if (mRecycled) { 1059 throw new RuntimeException("Cannot make calls to a recycled instance!"); 1060 } 1061 1062 final TypedValue value = mValue; 1063 if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { 1064 return mResources.getTextArray(value.resourceId); 1065 } 1066 return null; 1067 } 1068 1069 /** 1070 * Retrieve the raw TypedValue for the attribute at <var>index</var>. 1071 * 1072 * @param index Index of attribute to retrieve. 1073 * @param outValue TypedValue object in which to place the attribute's 1074 * data. 1075 * 1076 * @return {@code true} if the value was retrieved and not @empty, {@code false} otherwise. 1077 * @throws RuntimeException if the TypedArray has already been recycled. 1078 */ getValue(@tyleableRes int index, TypedValue outValue)1079 public boolean getValue(@StyleableRes int index, TypedValue outValue) { 1080 if (mRecycled) { 1081 throw new RuntimeException("Cannot make calls to a recycled instance!"); 1082 } 1083 1084 return getValueAt(index * STYLE_NUM_ENTRIES, outValue); 1085 } 1086 1087 /** 1088 * Returns the type of attribute at the specified index. 1089 * 1090 * @param index Index of attribute whose type to retrieve. 1091 * 1092 * @return Attribute type. 1093 * @throws RuntimeException if the TypedArray has already been recycled. 1094 */ getType(@tyleableRes int index)1095 public int getType(@StyleableRes int index) { 1096 if (mRecycled) { 1097 throw new RuntimeException("Cannot make calls to a recycled instance!"); 1098 } 1099 1100 index *= STYLE_NUM_ENTRIES; 1101 return mData[index + STYLE_TYPE]; 1102 } 1103 1104 /** 1105 * Returns the resource ID of the style or layout against which the specified attribute was 1106 * resolved, otherwise returns defValue. 1107 * 1108 * For example, if you we resolving two attributes {@code android:attribute1} and 1109 * {@code android:attribute2} and you were inflating a {@link android.view.View} from 1110 * {@code layout/my_layout.xml}: 1111 * <pre> 1112 * <View 1113 * style="@style/viewStyle" 1114 * android:layout_width="wrap_content" 1115 * android:layout_height="wrap_content" 1116 * android:attribute1="foo"/> 1117 * </pre> 1118 * 1119 * and {@code @style/viewStyle} is: 1120 * <pre> 1121 * <style android:name="viewStyle"> 1122 * <item name="android:attribute2">bar<item/> 1123 * <style/> 1124 * </pre> 1125 * 1126 * then resolved {@link TypedArray} will have values that return source resource ID of 1127 * {@code R.layout.my_layout} for {@code android:attribute1} and {@code R.style.viewStyle} for 1128 * {@code android:attribute2}. 1129 * 1130 * @param index Index of attribute whose source style to retrieve. 1131 * @param defaultValue Value to return if the attribute is not defined or 1132 * not a resource. 1133 * 1134 * @return Either a style resource ID, layout resource ID, or defaultValue if it was not 1135 * resolved in a style or layout. 1136 * @throws RuntimeException if the TypedArray has already been recycled. 1137 */ 1138 @AnyRes getSourceResourceId(@tyleableRes int index, @AnyRes int defaultValue)1139 public int getSourceResourceId(@StyleableRes int index, @AnyRes int defaultValue) { 1140 if (mRecycled) { 1141 throw new RuntimeException("Cannot make calls to a recycled instance!"); 1142 } 1143 1144 index *= STYLE_NUM_ENTRIES; 1145 final int resid = mData[index + STYLE_SOURCE_RESOURCE_ID]; 1146 if (resid != 0) { 1147 return resid; 1148 } 1149 return defaultValue; 1150 } 1151 1152 /** 1153 * Determines whether there is an attribute at <var>index</var>. 1154 * <p> 1155 * <strong>Note:</strong> If the attribute was set to {@code @empty} or 1156 * {@code @undefined}, this method returns {@code false}. 1157 * 1158 * @param index Index of attribute to retrieve. 1159 * 1160 * @return True if the attribute has a value, false otherwise. 1161 * @throws RuntimeException if the TypedArray has already been recycled. 1162 */ hasValue(@tyleableRes int index)1163 public boolean hasValue(@StyleableRes int index) { 1164 if (mRecycled) { 1165 throw new RuntimeException("Cannot make calls to a recycled instance!"); 1166 } 1167 1168 index *= STYLE_NUM_ENTRIES; 1169 final int[] data = mData; 1170 final int type = data[index + STYLE_TYPE]; 1171 return type != TypedValue.TYPE_NULL; 1172 } 1173 1174 /** 1175 * Determines whether there is an attribute at <var>index</var>, returning 1176 * {@code true} if the attribute was explicitly set to {@code @empty} and 1177 * {@code false} only if the attribute was undefined. 1178 * 1179 * @param index Index of attribute to retrieve. 1180 * 1181 * @return True if the attribute has a value or is empty, false otherwise. 1182 * @throws RuntimeException if the TypedArray has already been recycled. 1183 */ hasValueOrEmpty(@tyleableRes int index)1184 public boolean hasValueOrEmpty(@StyleableRes int index) { 1185 if (mRecycled) { 1186 throw new RuntimeException("Cannot make calls to a recycled instance!"); 1187 } 1188 1189 index *= STYLE_NUM_ENTRIES; 1190 final int[] data = mData; 1191 final int type = data[index + STYLE_TYPE]; 1192 return type != TypedValue.TYPE_NULL 1193 || data[index + STYLE_DATA] == TypedValue.DATA_NULL_EMPTY; 1194 } 1195 1196 /** 1197 * Retrieve the raw TypedValue for the attribute at <var>index</var> 1198 * and return a temporary object holding its data. This object is only 1199 * valid until the next call on to {@link TypedArray}. 1200 * 1201 * @param index Index of attribute to retrieve. 1202 * 1203 * @return Returns a TypedValue object if the attribute is defined, 1204 * containing its data; otherwise returns null. (You will not 1205 * receive a TypedValue whose type is TYPE_NULL.) 1206 * @throws RuntimeException if the TypedArray has already been recycled. 1207 */ peekValue(@tyleableRes int index)1208 public TypedValue peekValue(@StyleableRes int index) { 1209 if (mRecycled) { 1210 throw new RuntimeException("Cannot make calls to a recycled instance!"); 1211 } 1212 1213 final TypedValue value = mValue; 1214 if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { 1215 return value; 1216 } 1217 return null; 1218 } 1219 1220 /** 1221 * Returns a message about the parser state suitable for printing error messages. 1222 * 1223 * @return Human-readable description of current parser state. 1224 * @throws RuntimeException if the TypedArray has already been recycled. 1225 */ getPositionDescription()1226 public String getPositionDescription() { 1227 if (mRecycled) { 1228 throw new RuntimeException("Cannot make calls to a recycled instance!"); 1229 } 1230 1231 return mXml != null ? mXml.getPositionDescription() : "<internal>"; 1232 } 1233 1234 /** 1235 * Recycles the TypedArray, to be re-used by a later caller. After calling 1236 * this function you must not ever touch the typed array again. 1237 * 1238 * @throws RuntimeException if the TypedArray has already been recycled. 1239 */ recycle()1240 public void recycle() { 1241 if (mRecycled) { 1242 throw new RuntimeException(toString() + " recycled twice!"); 1243 } 1244 1245 mRecycled = true; 1246 1247 // These may have been set by the client. 1248 mXml = null; 1249 mTheme = null; 1250 mAssets = null; 1251 1252 mResources.mTypedArrayPool.release(this); 1253 } 1254 1255 /** 1256 * Recycles the TypedArray, to be re-used by a later caller. After calling 1257 * this function you must not ever touch the typed array again. 1258 * 1259 * @see #recycle() 1260 * @throws RuntimeException if the TypedArray has already been recycled. 1261 */ close()1262 public void close() { 1263 recycle(); 1264 } 1265 1266 /** 1267 * Extracts theme attributes from a typed array for later resolution using 1268 * {@link android.content.res.Resources.Theme#resolveAttributes(int[], int[])}. 1269 * Removes the entries from the typed array so that subsequent calls to typed 1270 * getters will return the default value without crashing. 1271 * 1272 * @return an array of length {@link #getIndexCount()} populated with theme 1273 * attributes, or null if there are no theme attributes in the typed 1274 * array 1275 * @throws RuntimeException if the TypedArray has already been recycled. 1276 * @hide 1277 */ 1278 @Nullable 1279 @UnsupportedAppUsage extractThemeAttrs()1280 public int[] extractThemeAttrs() { 1281 return extractThemeAttrs(null); 1282 } 1283 1284 /** 1285 * @hide 1286 */ 1287 @Nullable 1288 @UnsupportedAppUsage extractThemeAttrs(@ullable int[] scrap)1289 public int[] extractThemeAttrs(@Nullable int[] scrap) { 1290 if (mRecycled) { 1291 throw new RuntimeException("Cannot make calls to a recycled instance!"); 1292 } 1293 1294 int[] attrs = null; 1295 1296 final int[] data = mData; 1297 final int N = length(); 1298 for (int i = 0; i < N; i++) { 1299 final int index = i * STYLE_NUM_ENTRIES; 1300 if (data[index + STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) { 1301 // Not an attribute, ignore. 1302 continue; 1303 } 1304 1305 // Null the entry so that we can safely call getZzz(). 1306 data[index + STYLE_TYPE] = TypedValue.TYPE_NULL; 1307 1308 final int attr = data[index + STYLE_DATA]; 1309 if (attr == 0) { 1310 // Useless data, ignore. 1311 continue; 1312 } 1313 1314 // Ensure we have a usable attribute array. 1315 if (attrs == null) { 1316 if (scrap != null && scrap.length == N) { 1317 attrs = scrap; 1318 Arrays.fill(attrs, 0); 1319 } else { 1320 attrs = new int[N]; 1321 } 1322 } 1323 1324 attrs[i] = attr; 1325 } 1326 1327 return attrs; 1328 } 1329 1330 /** 1331 * Return a mask of the configuration parameters for which the values in 1332 * this typed array may change. 1333 * 1334 * @return Returns a mask of the changing configuration parameters, as 1335 * defined by {@link android.content.pm.ActivityInfo}. 1336 * @throws RuntimeException if the TypedArray has already been recycled. 1337 * @see android.content.pm.ActivityInfo 1338 */ getChangingConfigurations()1339 public @Config int getChangingConfigurations() { 1340 if (mRecycled) { 1341 throw new RuntimeException("Cannot make calls to a recycled instance!"); 1342 } 1343 1344 @Config int changingConfig = 0; 1345 1346 final int[] data = mData; 1347 final int N = length(); 1348 for (int i = 0; i < N; i++) { 1349 final int index = i * STYLE_NUM_ENTRIES; 1350 final int type = data[index + STYLE_TYPE]; 1351 if (type == TypedValue.TYPE_NULL) { 1352 continue; 1353 } 1354 changingConfig |= ActivityInfo.activityInfoConfigNativeToJava( 1355 data[index + STYLE_CHANGING_CONFIGURATIONS]); 1356 } 1357 return changingConfig; 1358 } 1359 1360 @UnsupportedAppUsage getValueAt(int index, TypedValue outValue)1361 private boolean getValueAt(int index, TypedValue outValue) { 1362 final int[] data = mData; 1363 final int type = data[index + STYLE_TYPE]; 1364 if (type == TypedValue.TYPE_NULL) { 1365 return false; 1366 } 1367 outValue.type = type; 1368 outValue.data = data[index + STYLE_DATA]; 1369 outValue.assetCookie = data[index + STYLE_ASSET_COOKIE]; 1370 outValue.resourceId = data[index + STYLE_RESOURCE_ID]; 1371 outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( 1372 data[index + STYLE_CHANGING_CONFIGURATIONS]); 1373 outValue.density = data[index + STYLE_DENSITY]; 1374 outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null; 1375 outValue.sourceResourceId = data[index + STYLE_SOURCE_RESOURCE_ID]; 1376 return true; 1377 } 1378 1379 @Nullable loadStringValueAt(int index)1380 private CharSequence loadStringValueAt(int index) { 1381 final int[] data = mData; 1382 final int cookie = data[index + STYLE_ASSET_COOKIE]; 1383 if (cookie < 0) { 1384 if (mXml != null) { 1385 return mXml.getPooledString(data[index + STYLE_DATA]); 1386 } 1387 return null; 1388 } 1389 return mAssets.getPooledStringForCookie(cookie, data[index + STYLE_DATA]); 1390 } 1391 1392 /** @hide */ TypedArray(Resources resources)1393 protected TypedArray(Resources resources) { 1394 mResources = resources; 1395 mMetrics = mResources.getDisplayMetrics(); 1396 mAssets = mResources.getAssets(); 1397 } 1398 1399 @Override toString()1400 public String toString() { 1401 return Arrays.toString(mData); 1402 } 1403 } 1404