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