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 + ", theme=" + mTheme); 532 } 533 534 throw new UnsupportedOperationException("Can't convert value at index " + attrIndex 535 + " to color: type=0x" + Integer.toHexString(type) + ", theme=" + mTheme); 536 } 537 538 /** 539 * Retrieve the ComplexColor for the attribute at <var>index</var>. 540 * The value may be either a {@link android.content.res.ColorStateList} which can wrap a simple 541 * color value or a {@link android.content.res.GradientColor} 542 * <p> 543 * This method will return {@code null} if the attribute is not defined or 544 * is not an integer color, color state list or GradientColor. 545 * 546 * @param index Index of attribute to retrieve. 547 * 548 * @return ComplexColor for the attribute, or {@code null} if not defined. 549 * @throws RuntimeException if the attribute if the TypedArray has already 550 * been recycled. 551 * @throws UnsupportedOperationException if the attribute is defined but is 552 * not an integer color, color state list or GradientColor. 553 * @hide 554 */ 555 @Nullable getComplexColor(@tyleableRes int index)556 public ComplexColor getComplexColor(@StyleableRes int index) { 557 if (mRecycled) { 558 throw new RuntimeException("Cannot make calls to a recycled instance!"); 559 } 560 561 final TypedValue value = mValue; 562 if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { 563 if (value.type == TypedValue.TYPE_ATTRIBUTE) { 564 throw new UnsupportedOperationException( 565 "Failed to resolve attribute at index " + index + ": " + value 566 + ", theme=" + mTheme); 567 } 568 return mResources.loadComplexColor(value, value.resourceId, mTheme); 569 } 570 return null; 571 } 572 573 /** 574 * Retrieve the ColorStateList for the attribute at <var>index</var>. 575 * The value may be either a single solid color or a reference to 576 * a color or complex {@link android.content.res.ColorStateList} 577 * description. 578 * <p> 579 * This method will return {@code null} if the attribute is not defined or 580 * is not an integer color or color state list. 581 * 582 * @param index Index of attribute to retrieve. 583 * 584 * @return ColorStateList for the attribute, or {@code null} if not 585 * defined. 586 * @throws RuntimeException if the attribute if the TypedArray has already 587 * been recycled. 588 * @throws UnsupportedOperationException if the attribute is defined but is 589 * not an integer color or color state list. 590 */ 591 @Nullable getColorStateList(@tyleableRes int index)592 public ColorStateList getColorStateList(@StyleableRes int index) { 593 if (mRecycled) { 594 throw new RuntimeException("Cannot make calls to a recycled instance!"); 595 } 596 597 final TypedValue value = mValue; 598 if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { 599 if (value.type == TypedValue.TYPE_ATTRIBUTE) { 600 throw new UnsupportedOperationException( 601 "Failed to resolve attribute at index " + index + ": " + value 602 + ", theme=" + mTheme); 603 } 604 return mResources.loadColorStateList(value, value.resourceId, mTheme); 605 } 606 return null; 607 } 608 609 /** 610 * Retrieve the integer value for the attribute at <var>index</var>. 611 * <p> 612 * Unlike {@link #getInt(int, int)}, this method will throw an exception if 613 * the attribute is defined but is not an integer. 614 * 615 * @param index Index of attribute to retrieve. 616 * @param defValue Value to return if the attribute is not defined or 617 * not a resource. 618 * 619 * @return Attribute integer value, or defValue if not defined. 620 * @throws RuntimeException if the TypedArray has already been recycled. 621 * @throws UnsupportedOperationException if the attribute is defined but is 622 * not an integer. 623 */ getInteger(@tyleableRes int index, int defValue)624 public int getInteger(@StyleableRes int index, int defValue) { 625 if (mRecycled) { 626 throw new RuntimeException("Cannot make calls to a recycled instance!"); 627 } 628 629 final int attrIndex = index; 630 index *= STYLE_NUM_ENTRIES; 631 632 final int[] data = mData; 633 final int type = data[index + STYLE_TYPE]; 634 if (type == TypedValue.TYPE_NULL) { 635 return defValue; 636 } else if (type >= TypedValue.TYPE_FIRST_INT 637 && type <= TypedValue.TYPE_LAST_INT) { 638 return data[index + STYLE_DATA]; 639 } else if (type == TypedValue.TYPE_ATTRIBUTE) { 640 final TypedValue value = mValue; 641 getValueAt(index, value); 642 throw new UnsupportedOperationException( 643 "Failed to resolve attribute at index " + attrIndex + ": " + value 644 + ", theme=" + mTheme); 645 } 646 647 throw new UnsupportedOperationException("Can't convert value at index " + attrIndex 648 + " to integer: type=0x" + Integer.toHexString(type) + ", theme=" + mTheme); 649 } 650 651 /** 652 * Retrieve a dimensional unit attribute at <var>index</var>. Unit 653 * conversions are based on the current {@link DisplayMetrics} 654 * associated with the resources this {@link TypedArray} object 655 * came from. 656 * <p> 657 * This method will throw an exception if the attribute is defined but is 658 * not a dimension. 659 * 660 * @param index Index of attribute to retrieve. 661 * @param defValue Value to return if the attribute is not defined or 662 * not a resource. 663 * 664 * @return Attribute dimension value multiplied by the appropriate 665 * metric, or defValue if not defined. 666 * @throws RuntimeException if the TypedArray has already been recycled. 667 * @throws UnsupportedOperationException if the attribute is defined but is 668 * not an integer. 669 * 670 * @see #getDimensionPixelOffset 671 * @see #getDimensionPixelSize 672 */ getDimension(@tyleableRes int index, float defValue)673 public float getDimension(@StyleableRes int index, float defValue) { 674 if (mRecycled) { 675 throw new RuntimeException("Cannot make calls to a recycled instance!"); 676 } 677 678 final int attrIndex = index; 679 index *= STYLE_NUM_ENTRIES; 680 681 final int[] data = mData; 682 final int type = data[index + STYLE_TYPE]; 683 if (type == TypedValue.TYPE_NULL) { 684 return defValue; 685 } else if (type == TypedValue.TYPE_DIMENSION) { 686 return TypedValue.complexToDimension(data[index + STYLE_DATA], mMetrics); 687 } else if (type == TypedValue.TYPE_ATTRIBUTE) { 688 final TypedValue value = mValue; 689 getValueAt(index, value); 690 throw new UnsupportedOperationException( 691 "Failed to resolve attribute at index " + attrIndex + ": " + value 692 + ", theme=" + mTheme); 693 } 694 695 throw new UnsupportedOperationException("Can't convert value at index " + attrIndex 696 + " to dimension: type=0x" + Integer.toHexString(type) + ", theme=" + mTheme); 697 } 698 699 /** 700 * Retrieve a dimensional unit attribute at <var>index</var> for use 701 * as an offset in raw pixels. This is the same as 702 * {@link #getDimension}, except the returned value is converted to 703 * integer pixels for you. An offset conversion involves simply 704 * truncating the base value to an integer. 705 * <p> 706 * This method will throw an exception if the attribute is defined but is 707 * not a dimension. 708 * 709 * @param index Index of attribute to retrieve. 710 * @param defValue Value to return if the attribute is not defined or 711 * not a resource. 712 * 713 * @return Attribute dimension value multiplied by the appropriate 714 * metric and truncated to integer pixels, or defValue if not defined. 715 * @throws RuntimeException if the TypedArray has already been recycled. 716 * @throws UnsupportedOperationException if the attribute is defined but is 717 * not an integer. 718 * 719 * @see #getDimension 720 * @see #getDimensionPixelSize 721 */ getDimensionPixelOffset(@tyleableRes int index, int defValue)722 public int getDimensionPixelOffset(@StyleableRes int index, int defValue) { 723 if (mRecycled) { 724 throw new RuntimeException("Cannot make calls to a recycled instance!"); 725 } 726 727 final int attrIndex = index; 728 index *= STYLE_NUM_ENTRIES; 729 730 final int[] data = mData; 731 final int type = data[index + STYLE_TYPE]; 732 if (type == TypedValue.TYPE_NULL) { 733 return defValue; 734 } else if (type == TypedValue.TYPE_DIMENSION) { 735 return TypedValue.complexToDimensionPixelOffset(data[index + STYLE_DATA], mMetrics); 736 } else if (type == TypedValue.TYPE_ATTRIBUTE) { 737 final TypedValue value = mValue; 738 getValueAt(index, value); 739 throw new UnsupportedOperationException( 740 "Failed to resolve attribute at index " + attrIndex + ": " + value 741 + ", theme=" + mTheme); 742 } 743 744 throw new UnsupportedOperationException("Can't convert value at index " + attrIndex 745 + " to dimension: type=0x" + Integer.toHexString(type) + ", theme=" + mTheme); 746 } 747 748 /** 749 * Retrieve a dimensional unit attribute at <var>index</var> for use 750 * as a size in raw pixels. This is the same as 751 * {@link #getDimension}, except the returned value is converted to 752 * integer pixels for use as a size. A size conversion involves 753 * rounding the base value, and ensuring that a non-zero base value 754 * is at least one pixel in size. 755 * <p> 756 * This method will throw an exception if the attribute is defined but is 757 * not a dimension. 758 * 759 * @param index Index of attribute to retrieve. 760 * @param defValue Value to return if the attribute is not defined or 761 * not a resource. 762 * 763 * @return Attribute dimension value multiplied by the appropriate 764 * metric and truncated to integer pixels, or defValue if not defined. 765 * @throws RuntimeException if the TypedArray has already been recycled. 766 * @throws UnsupportedOperationException if the attribute is defined but is 767 * not a dimension. 768 * 769 * @see #getDimension 770 * @see #getDimensionPixelOffset 771 */ getDimensionPixelSize(@tyleableRes int index, int defValue)772 public int getDimensionPixelSize(@StyleableRes int index, int defValue) { 773 if (mRecycled) { 774 throw new RuntimeException("Cannot make calls to a recycled instance!"); 775 } 776 777 final int attrIndex = index; 778 index *= STYLE_NUM_ENTRIES; 779 780 final int[] data = mData; 781 final int type = data[index + STYLE_TYPE]; 782 if (type == TypedValue.TYPE_NULL) { 783 return defValue; 784 } else if (type == TypedValue.TYPE_DIMENSION) { 785 return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); 786 } else if (type == TypedValue.TYPE_ATTRIBUTE) { 787 final TypedValue value = mValue; 788 getValueAt(index, value); 789 throw new UnsupportedOperationException( 790 "Failed to resolve attribute at index " + attrIndex + ": " + value 791 + ", theme=" + mTheme); 792 } 793 794 throw new UnsupportedOperationException("Can't convert value at index " + attrIndex 795 + " to dimension: type=0x" + Integer.toHexString(type) + ", theme=" + mTheme); 796 } 797 798 /** 799 * Special version of {@link #getDimensionPixelSize} for retrieving 800 * {@link android.view.ViewGroup}'s layout_width and layout_height 801 * attributes. This is only here for performance reasons; applications 802 * should use {@link #getDimensionPixelSize}. 803 * <p> 804 * This method will throw an exception if the attribute is defined but is 805 * not a dimension or integer (enum). 806 * 807 * @param index Index of the attribute to retrieve. 808 * @param name Textual name of attribute for error reporting. 809 * 810 * @return Attribute dimension value multiplied by the appropriate 811 * metric and truncated to integer pixels. 812 * @throws RuntimeException if the TypedArray has already been recycled. 813 * @throws UnsupportedOperationException if the attribute is defined but is 814 * not a dimension or integer (enum). 815 */ getLayoutDimension(@tyleableRes int index, String name)816 public int getLayoutDimension(@StyleableRes int index, String name) { 817 if (mRecycled) { 818 throw new RuntimeException("Cannot make calls to a recycled instance!"); 819 } 820 821 final int attrIndex = index; 822 index *= STYLE_NUM_ENTRIES; 823 824 final int[] data = mData; 825 final int type = data[index + STYLE_TYPE]; 826 if (type >= TypedValue.TYPE_FIRST_INT 827 && type <= TypedValue.TYPE_LAST_INT) { 828 return data[index + STYLE_DATA]; 829 } else if (type == TypedValue.TYPE_DIMENSION) { 830 return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); 831 } else if (type == TypedValue.TYPE_ATTRIBUTE) { 832 final TypedValue value = mValue; 833 getValueAt(index, value); 834 throw new UnsupportedOperationException( 835 "Failed to resolve attribute at index " + attrIndex + ": " + value 836 + ", theme=" + mTheme); 837 } 838 839 throw new UnsupportedOperationException(getPositionDescription() 840 + ": You must supply a " + name + " attribute." + ", theme=" + mTheme); 841 } 842 843 /** 844 * Special version of {@link #getDimensionPixelSize} for retrieving 845 * {@link android.view.ViewGroup}'s layout_width and layout_height 846 * attributes. This is only here for performance reasons; applications 847 * should use {@link #getDimensionPixelSize}. 848 * 849 * @param index Index of the attribute to retrieve. 850 * @param defValue The default value to return if this attribute is not 851 * default or contains the wrong type of data. 852 * 853 * @return Attribute dimension value multiplied by the appropriate 854 * metric and truncated to integer pixels. 855 * @throws RuntimeException if the TypedArray has already been recycled. 856 */ getLayoutDimension(@tyleableRes int index, int defValue)857 public int getLayoutDimension(@StyleableRes int index, int defValue) { 858 if (mRecycled) { 859 throw new RuntimeException("Cannot make calls to a recycled instance!"); 860 } 861 862 index *= STYLE_NUM_ENTRIES; 863 final int[] data = mData; 864 final int type = data[index + STYLE_TYPE]; 865 if (type >= TypedValue.TYPE_FIRST_INT 866 && type <= TypedValue.TYPE_LAST_INT) { 867 return data[index + STYLE_DATA]; 868 } else if (type == TypedValue.TYPE_DIMENSION) { 869 return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); 870 } 871 872 return defValue; 873 } 874 875 /** 876 * Retrieves a fractional unit attribute at <var>index</var>. 877 * 878 * @param index Index of attribute to retrieve. 879 * @param base The base value of this fraction. In other words, a 880 * standard fraction is multiplied by this value. 881 * @param pbase The parent base value of this fraction. In other 882 * words, a parent fraction (nn%p) is multiplied by this 883 * value. 884 * @param defValue Value to return if the attribute is not defined or 885 * not a resource. 886 * 887 * @return Attribute fractional value multiplied by the appropriate 888 * base value, or defValue if not defined. 889 * @throws RuntimeException if the TypedArray has already been recycled. 890 * @throws UnsupportedOperationException if the attribute is defined but is 891 * not a fraction. 892 */ getFraction(@tyleableRes int index, int base, int pbase, float defValue)893 public float getFraction(@StyleableRes int index, int base, int pbase, float defValue) { 894 if (mRecycled) { 895 throw new RuntimeException("Cannot make calls to a recycled instance!"); 896 } 897 898 final int attrIndex = index; 899 index *= STYLE_NUM_ENTRIES; 900 901 final int[] data = mData; 902 final int type = data[index + STYLE_TYPE]; 903 if (type == TypedValue.TYPE_NULL) { 904 return defValue; 905 } else if (type == TypedValue.TYPE_FRACTION) { 906 return TypedValue.complexToFraction(data[index + STYLE_DATA], base, pbase); 907 } else if (type == TypedValue.TYPE_ATTRIBUTE) { 908 final TypedValue value = mValue; 909 getValueAt(index, value); 910 throw new UnsupportedOperationException( 911 "Failed to resolve attribute at index " + attrIndex + ": " + value 912 + ", theme=" + mTheme); 913 } 914 915 throw new UnsupportedOperationException("Can't convert value at index " + attrIndex 916 + " to fraction: type=0x" + Integer.toHexString(type) + ", theme=" + mTheme); 917 } 918 919 /** 920 * Retrieves the resource identifier for the attribute at 921 * <var>index</var>. Note that attribute resource as resolved when 922 * the overall {@link TypedArray} object is retrieved. As a 923 * result, this function will return the resource identifier of the 924 * final resource value that was found, <em>not</em> necessarily the 925 * original resource that was specified by the attribute. 926 * 927 * @param index Index of attribute to retrieve. 928 * @param defValue Value to return if the attribute is not defined or 929 * not a resource. 930 * 931 * @return Attribute resource identifier, or defValue if not defined. 932 * @throws RuntimeException if the TypedArray has already been recycled. 933 */ 934 @AnyRes getResourceId(@tyleableRes int index, int defValue)935 public int getResourceId(@StyleableRes int index, int defValue) { 936 if (mRecycled) { 937 throw new RuntimeException("Cannot make calls to a recycled instance!"); 938 } 939 940 index *= STYLE_NUM_ENTRIES; 941 final int[] data = mData; 942 if (data[index + STYLE_TYPE] != TypedValue.TYPE_NULL) { 943 final int resid = data[index + STYLE_RESOURCE_ID]; 944 if (resid != 0) { 945 return resid; 946 } 947 } 948 return defValue; 949 } 950 951 /** 952 * Retrieves the theme attribute resource identifier for the attribute at 953 * <var>index</var>. 954 * 955 * @param index Index of attribute to retrieve. 956 * @param defValue Value to return if the attribute is not defined or not a 957 * resource. 958 * 959 * @return Theme attribute resource identifier, or defValue if not defined. 960 * @throws RuntimeException if the TypedArray has already been recycled. 961 * @hide 962 */ getThemeAttributeId(@tyleableRes int index, int defValue)963 public int getThemeAttributeId(@StyleableRes int index, int defValue) { 964 if (mRecycled) { 965 throw new RuntimeException("Cannot make calls to a recycled instance!"); 966 } 967 968 index *= STYLE_NUM_ENTRIES; 969 final int[] data = mData; 970 if (data[index + STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) { 971 return data[index + STYLE_DATA]; 972 } 973 return defValue; 974 } 975 976 /** 977 * Retrieve the Drawable for the attribute at <var>index</var>. 978 * <p> 979 * This method will throw an exception if the attribute is defined but is 980 * not a color or drawable resource. 981 * 982 * @param index Index of attribute to retrieve. 983 * 984 * @return Drawable for the attribute, or {@code null} if not defined. 985 * @throws RuntimeException if the TypedArray has already been recycled. 986 * @throws UnsupportedOperationException if the attribute is defined but is 987 * not a color or drawable resource. 988 */ 989 @Nullable getDrawable(@tyleableRes int index)990 public Drawable getDrawable(@StyleableRes int index) { 991 return getDrawableForDensity(index, 0); 992 } 993 994 /** 995 * Version of {@link #getDrawable(int)} that accepts an override density. 996 * @hide 997 */ 998 @Nullable getDrawableForDensity(@tyleableRes int index, int density)999 public Drawable getDrawableForDensity(@StyleableRes int index, int density) { 1000 if (mRecycled) { 1001 throw new RuntimeException("Cannot make calls to a recycled instance!"); 1002 } 1003 1004 final TypedValue value = mValue; 1005 if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { 1006 if (value.type == TypedValue.TYPE_ATTRIBUTE) { 1007 throw new UnsupportedOperationException( 1008 "Failed to resolve attribute at index " + index + ": " + value 1009 + ", theme=" + mTheme); 1010 } 1011 1012 if (density > 0) { 1013 // If the density is overridden, the value in the TypedArray will not reflect this. 1014 // Do a separate lookup of the resourceId with the density override. 1015 mResources.getValueForDensity(value.resourceId, density, value, true); 1016 } 1017 return mResources.loadDrawable(value, value.resourceId, density, mTheme); 1018 } 1019 return null; 1020 } 1021 1022 /** 1023 * Retrieve the Typeface for the attribute at <var>index</var>. 1024 * <p> 1025 * This method will throw an exception if the attribute is defined but is 1026 * not a font. 1027 * 1028 * @param index Index of attribute to retrieve. 1029 * 1030 * @return Typeface for the attribute, or {@code null} if not defined. 1031 * @throws RuntimeException if the TypedArray has already been recycled. 1032 * @throws UnsupportedOperationException if the attribute is defined but is 1033 * not a font resource. 1034 */ 1035 @Nullable getFont(@tyleableRes int index)1036 public Typeface getFont(@StyleableRes int index) { 1037 if (mRecycled) { 1038 throw new RuntimeException("Cannot make calls to a recycled instance!"); 1039 } 1040 1041 final TypedValue value = mValue; 1042 if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { 1043 if (value.type == TypedValue.TYPE_ATTRIBUTE) { 1044 throw new UnsupportedOperationException( 1045 "Failed to resolve attribute at index " + index + ": " + value 1046 + ", theme=" + mTheme); 1047 } 1048 return mResources.getFont(value, value.resourceId); 1049 } 1050 return null; 1051 } 1052 1053 /** 1054 * Retrieve the CharSequence[] for the attribute at <var>index</var>. 1055 * This gets the resource ID of the selected attribute, and uses 1056 * {@link Resources#getTextArray Resources.getTextArray} of the owning 1057 * Resources object to retrieve its String[]. 1058 * <p> 1059 * This method will throw an exception if the attribute is defined but is 1060 * not a text array resource. 1061 * 1062 * @param index Index of attribute to retrieve. 1063 * 1064 * @return CharSequence[] for the attribute, or {@code null} if not 1065 * defined. 1066 * @throws RuntimeException if the TypedArray has already been recycled. 1067 */ getTextArray(@tyleableRes int index)1068 public CharSequence[] getTextArray(@StyleableRes int index) { 1069 if (mRecycled) { 1070 throw new RuntimeException("Cannot make calls to a recycled instance!"); 1071 } 1072 1073 final TypedValue value = mValue; 1074 if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { 1075 return mResources.getTextArray(value.resourceId); 1076 } 1077 return null; 1078 } 1079 1080 /** 1081 * Retrieve the raw TypedValue for the attribute at <var>index</var>. 1082 * 1083 * @param index Index of attribute to retrieve. 1084 * @param outValue TypedValue object in which to place the attribute's 1085 * data. 1086 * 1087 * @return {@code true} if the value was retrieved and not @empty, {@code false} otherwise. 1088 * @throws RuntimeException if the TypedArray has already been recycled. 1089 */ getValue(@tyleableRes int index, TypedValue outValue)1090 public boolean getValue(@StyleableRes int index, TypedValue outValue) { 1091 if (mRecycled) { 1092 throw new RuntimeException("Cannot make calls to a recycled instance!"); 1093 } 1094 1095 return getValueAt(index * STYLE_NUM_ENTRIES, outValue); 1096 } 1097 1098 /** 1099 * Returns the type of attribute at the specified index. 1100 * 1101 * @param index Index of attribute whose type to retrieve. 1102 * 1103 * @return Attribute type. 1104 * @throws RuntimeException if the TypedArray has already been recycled. 1105 */ getType(@tyleableRes int index)1106 public int getType(@StyleableRes int index) { 1107 if (mRecycled) { 1108 throw new RuntimeException("Cannot make calls to a recycled instance!"); 1109 } 1110 1111 index *= STYLE_NUM_ENTRIES; 1112 return mData[index + STYLE_TYPE]; 1113 } 1114 1115 /** 1116 * Returns the resource ID of the style or layout against which the specified attribute was 1117 * resolved, otherwise returns defValue. 1118 * 1119 * For example, if you we resolving two attributes {@code android:attribute1} and 1120 * {@code android:attribute2} and you were inflating a {@link android.view.View} from 1121 * {@code layout/my_layout.xml}: 1122 * <pre> 1123 * <View 1124 * style="@style/viewStyle" 1125 * android:layout_width="wrap_content" 1126 * android:layout_height="wrap_content" 1127 * android:attribute1="foo"/> 1128 * </pre> 1129 * 1130 * and {@code @style/viewStyle} is: 1131 * <pre> 1132 * <style android:name="viewStyle"> 1133 * <item name="android:attribute2">bar<item/> 1134 * <style/> 1135 * </pre> 1136 * 1137 * then resolved {@link TypedArray} will have values that return source resource ID of 1138 * {@code R.layout.my_layout} for {@code android:attribute1} and {@code R.style.viewStyle} for 1139 * {@code android:attribute2}. 1140 * 1141 * @param index Index of attribute whose source style to retrieve. 1142 * @param defaultValue Value to return if the attribute is not defined or 1143 * not a resource. 1144 * 1145 * @return Either a style resource ID, layout resource ID, or defaultValue if it was not 1146 * resolved in a style or layout. 1147 * @throws RuntimeException if the TypedArray has already been recycled. 1148 */ 1149 @AnyRes getSourceResourceId(@tyleableRes int index, @AnyRes int defaultValue)1150 public int getSourceResourceId(@StyleableRes int index, @AnyRes int defaultValue) { 1151 if (mRecycled) { 1152 throw new RuntimeException("Cannot make calls to a recycled instance!"); 1153 } 1154 1155 index *= STYLE_NUM_ENTRIES; 1156 final int resid = mData[index + STYLE_SOURCE_RESOURCE_ID]; 1157 if (resid != 0) { 1158 return resid; 1159 } 1160 return defaultValue; 1161 } 1162 1163 /** 1164 * Determines whether there is an attribute at <var>index</var>. 1165 * <p> 1166 * <strong>Note:</strong> If the attribute was set to {@code @empty} or 1167 * {@code @undefined}, this method returns {@code false}. 1168 * 1169 * @param index Index of attribute to retrieve. 1170 * 1171 * @return True if the attribute has a value, false otherwise. 1172 * @throws RuntimeException if the TypedArray has already been recycled. 1173 */ hasValue(@tyleableRes int index)1174 public boolean hasValue(@StyleableRes int index) { 1175 if (mRecycled) { 1176 throw new RuntimeException("Cannot make calls to a recycled instance!"); 1177 } 1178 1179 index *= STYLE_NUM_ENTRIES; 1180 final int[] data = mData; 1181 final int type = data[index + STYLE_TYPE]; 1182 return type != TypedValue.TYPE_NULL; 1183 } 1184 1185 /** 1186 * Determines whether there is an attribute at <var>index</var>, returning 1187 * {@code true} if the attribute was explicitly set to {@code @empty} and 1188 * {@code false} only if the attribute was undefined. 1189 * 1190 * @param index Index of attribute to retrieve. 1191 * 1192 * @return True if the attribute has a value or is empty, false otherwise. 1193 * @throws RuntimeException if the TypedArray has already been recycled. 1194 */ hasValueOrEmpty(@tyleableRes int index)1195 public boolean hasValueOrEmpty(@StyleableRes int index) { 1196 if (mRecycled) { 1197 throw new RuntimeException("Cannot make calls to a recycled instance!"); 1198 } 1199 1200 index *= STYLE_NUM_ENTRIES; 1201 final int[] data = mData; 1202 final int type = data[index + STYLE_TYPE]; 1203 return type != TypedValue.TYPE_NULL 1204 || data[index + STYLE_DATA] == TypedValue.DATA_NULL_EMPTY; 1205 } 1206 1207 /** 1208 * Retrieve the raw TypedValue for the attribute at <var>index</var> 1209 * and return a temporary object holding its data. This object is only 1210 * valid until the next call on to {@link TypedArray}. 1211 * 1212 * @param index Index of attribute to retrieve. 1213 * 1214 * @return Returns a TypedValue object if the attribute is defined, 1215 * containing its data; otherwise returns null. (You will not 1216 * receive a TypedValue whose type is TYPE_NULL.) 1217 * @throws RuntimeException if the TypedArray has already been recycled. 1218 */ peekValue(@tyleableRes int index)1219 public TypedValue peekValue(@StyleableRes int index) { 1220 if (mRecycled) { 1221 throw new RuntimeException("Cannot make calls to a recycled instance!"); 1222 } 1223 1224 final TypedValue value = mValue; 1225 if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { 1226 return value; 1227 } 1228 return null; 1229 } 1230 1231 /** 1232 * Returns a message about the parser state suitable for printing error messages. 1233 * 1234 * @return Human-readable description of current parser state. 1235 * @throws RuntimeException if the TypedArray has already been recycled. 1236 */ getPositionDescription()1237 public String getPositionDescription() { 1238 if (mRecycled) { 1239 throw new RuntimeException("Cannot make calls to a recycled instance!"); 1240 } 1241 1242 return mXml != null ? mXml.getPositionDescription() : "<internal>"; 1243 } 1244 1245 /** 1246 * Recycles the TypedArray, to be re-used by a later caller. After calling 1247 * this function you must not ever touch the typed array again. 1248 * 1249 * @throws RuntimeException if the TypedArray has already been recycled. 1250 */ recycle()1251 public void recycle() { 1252 if (mRecycled) { 1253 throw new RuntimeException(toString() + " recycled twice!"); 1254 } 1255 1256 mRecycled = true; 1257 1258 // These may have been set by the client. 1259 mXml = null; 1260 mTheme = null; 1261 mAssets = null; 1262 1263 mResources.mTypedArrayPool.release(this); 1264 } 1265 1266 /** 1267 * Recycles the TypedArray, to be re-used by a later caller. After calling 1268 * this function you must not ever touch the typed array again. 1269 * 1270 * @see #recycle() 1271 * @throws RuntimeException if the TypedArray has already been recycled. 1272 */ close()1273 public void close() { 1274 recycle(); 1275 } 1276 1277 /** 1278 * Extracts theme attributes from a typed array for later resolution using 1279 * {@link android.content.res.Resources.Theme#resolveAttributes(int[], int[])}. 1280 * Removes the entries from the typed array so that subsequent calls to typed 1281 * getters will return the default value without crashing. 1282 * 1283 * @return an array of length {@link #getIndexCount()} populated with theme 1284 * attributes, or null if there are no theme attributes in the typed 1285 * array 1286 * @throws RuntimeException if the TypedArray has already been recycled. 1287 * @hide 1288 */ 1289 @Nullable 1290 @UnsupportedAppUsage extractThemeAttrs()1291 public int[] extractThemeAttrs() { 1292 return extractThemeAttrs(null); 1293 } 1294 1295 /** 1296 * @hide 1297 */ 1298 @Nullable 1299 @UnsupportedAppUsage extractThemeAttrs(@ullable int[] scrap)1300 public int[] extractThemeAttrs(@Nullable int[] scrap) { 1301 if (mRecycled) { 1302 throw new RuntimeException("Cannot make calls to a recycled instance!"); 1303 } 1304 1305 int[] attrs = null; 1306 1307 final int[] data = mData; 1308 final int N = length(); 1309 for (int i = 0; i < N; i++) { 1310 final int index = i * STYLE_NUM_ENTRIES; 1311 if (data[index + STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) { 1312 // Not an attribute, ignore. 1313 continue; 1314 } 1315 1316 // Null the entry so that we can safely call getZzz(). 1317 data[index + STYLE_TYPE] = TypedValue.TYPE_NULL; 1318 1319 final int attr = data[index + STYLE_DATA]; 1320 if (attr == 0) { 1321 // Useless data, ignore. 1322 continue; 1323 } 1324 1325 // Ensure we have a usable attribute array. 1326 if (attrs == null) { 1327 if (scrap != null && scrap.length == N) { 1328 attrs = scrap; 1329 Arrays.fill(attrs, 0); 1330 } else { 1331 attrs = new int[N]; 1332 } 1333 } 1334 1335 attrs[i] = attr; 1336 } 1337 1338 return attrs; 1339 } 1340 1341 /** 1342 * Return a mask of the configuration parameters for which the values in 1343 * this typed array may change. 1344 * 1345 * @return Returns a mask of the changing configuration parameters, as 1346 * defined by {@link android.content.pm.ActivityInfo}. 1347 * @throws RuntimeException if the TypedArray has already been recycled. 1348 * @see android.content.pm.ActivityInfo 1349 */ getChangingConfigurations()1350 public @Config int getChangingConfigurations() { 1351 if (mRecycled) { 1352 throw new RuntimeException("Cannot make calls to a recycled instance!"); 1353 } 1354 1355 @Config int changingConfig = 0; 1356 1357 final int[] data = mData; 1358 final int N = length(); 1359 for (int i = 0; i < N; i++) { 1360 final int index = i * STYLE_NUM_ENTRIES; 1361 final int type = data[index + STYLE_TYPE]; 1362 if (type == TypedValue.TYPE_NULL) { 1363 continue; 1364 } 1365 changingConfig |= ActivityInfo.activityInfoConfigNativeToJava( 1366 data[index + STYLE_CHANGING_CONFIGURATIONS]); 1367 } 1368 return changingConfig; 1369 } 1370 1371 @UnsupportedAppUsage getValueAt(int index, TypedValue outValue)1372 private boolean getValueAt(int index, TypedValue outValue) { 1373 final int[] data = mData; 1374 final int type = data[index + STYLE_TYPE]; 1375 if (type == TypedValue.TYPE_NULL) { 1376 return false; 1377 } 1378 outValue.type = type; 1379 outValue.data = data[index + STYLE_DATA]; 1380 outValue.assetCookie = data[index + STYLE_ASSET_COOKIE]; 1381 outValue.resourceId = data[index + STYLE_RESOURCE_ID]; 1382 outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( 1383 data[index + STYLE_CHANGING_CONFIGURATIONS]); 1384 outValue.density = data[index + STYLE_DENSITY]; 1385 outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null; 1386 outValue.sourceResourceId = data[index + STYLE_SOURCE_RESOURCE_ID]; 1387 return true; 1388 } 1389 1390 @Nullable loadStringValueAt(int index)1391 private CharSequence loadStringValueAt(int index) { 1392 final int[] data = mData; 1393 final int cookie = data[index + STYLE_ASSET_COOKIE]; 1394 if (cookie < 0) { 1395 if (mXml != null) { 1396 return mXml.getPooledString(data[index + STYLE_DATA]); 1397 } 1398 return null; 1399 } 1400 return mAssets.getPooledStringForCookie(cookie, data[index + STYLE_DATA]); 1401 } 1402 1403 /** @hide */ TypedArray(Resources resources)1404 protected TypedArray(Resources resources) { 1405 mResources = resources; 1406 mMetrics = mResources.getDisplayMetrics(); 1407 mAssets = mResources.getAssets(); 1408 } 1409 1410 @Override toString()1411 public String toString() { 1412 return Arrays.toString(mData); 1413 } 1414 } 1415