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