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