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