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