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