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