BridgeTypedArray.java revision bc34eb11f67c46b0808d2a65628077faed0eefb0
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 17package android.content.res; 18 19import com.android.ide.common.rendering.api.AttrResourceValue; 20import com.android.ide.common.rendering.api.LayoutLog; 21import com.android.ide.common.rendering.api.RenderResources; 22import com.android.ide.common.rendering.api.ResourceValue; 23import com.android.ide.common.rendering.api.StyleResourceValue; 24import com.android.internal.util.XmlUtils; 25import com.android.layoutlib.bridge.Bridge; 26import com.android.layoutlib.bridge.BridgeConstants; 27import com.android.layoutlib.bridge.android.BridgeContext; 28import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; 29import com.android.layoutlib.bridge.impl.ParserFactory; 30import com.android.layoutlib.bridge.impl.ResourceHelper; 31import com.android.resources.ResourceType; 32 33import org.xmlpull.v1.XmlPullParser; 34import org.xmlpull.v1.XmlPullParserException; 35 36import android.graphics.drawable.Drawable; 37import android.util.DisplayMetrics; 38import android.util.TypedValue; 39import android.view.LayoutInflater_Delegate; 40import android.view.ViewGroup.LayoutParams; 41 42import java.io.File; 43import java.util.Arrays; 44import java.util.Map; 45 46/** 47 * Custom implementation of TypedArray to handle non compiled resources. 48 */ 49public final class BridgeTypedArray extends TypedArray { 50 51 private final BridgeResources mBridgeResources; 52 private final BridgeContext mContext; 53 private final boolean mPlatformFile; 54 55 private final ResourceValue[] mResourceData; 56 private final String[] mNames; 57 private final boolean[] mIsFramework; 58 59 public BridgeTypedArray(BridgeResources resources, BridgeContext context, int len, 60 boolean platformFile) { 61 super(resources, null, null, 0); 62 mBridgeResources = resources; 63 mContext = context; 64 mPlatformFile = platformFile; 65 mResourceData = new ResourceValue[len]; 66 mNames = new String[len]; 67 mIsFramework = new boolean[len]; 68 } 69 70 /** 71 * A bridge-specific method that sets a value in the type array 72 * @param index the index of the value in the TypedArray 73 * @param name the name of the attribute 74 * @param isFramework whether the attribute is in the android namespace. 75 * @param value the value of the attribute 76 */ 77 public void bridgeSetValue(int index, String name, boolean isFramework, ResourceValue value) { 78 mResourceData[index] = value; 79 mNames[index] = name; 80 mIsFramework[index] = isFramework; 81 } 82 83 /** 84 * Seals the array after all calls to 85 * {@link #bridgeSetValue(int, String, boolean, ResourceValue)} have been done. 86 * <p/>This allows to compute the list of non default values, permitting 87 * {@link #getIndexCount()} to return the proper value. 88 */ 89 public void sealArray() { 90 // fills TypedArray.mIndices which is used to implement getIndexCount/getIndexAt 91 // first count the array size 92 int count = 0; 93 for (int i = 0; i < mResourceData.length; i++) { 94 ResourceValue data = mResourceData[i]; 95 if (data != null) { 96 if (RenderResources.REFERENCE_NULL.equals(data.getValue())) { 97 // No need to store this resource value. This saves needless checking for 98 // "@null" every time an attribute is requested. 99 mResourceData[i] = null; 100 } else { 101 count++; 102 } 103 } 104 } 105 106 // allocate the table with an extra to store the size 107 mIndices = new int[count+1]; 108 mIndices[0] = count; 109 110 // fill the array with the indices. 111 int index = 1; 112 for (int i = 0 ; i < mResourceData.length ; i++) { 113 if (mResourceData[i] != null) { 114 mIndices[index++] = i; 115 } 116 } 117 } 118 119 /** 120 * Return the number of values in this array. 121 */ 122 @Override 123 public int length() { 124 return mResourceData.length; 125 } 126 127 /** 128 * Return the Resources object this array was loaded from. 129 */ 130 @Override 131 public Resources getResources() { 132 return mBridgeResources; 133 } 134 135 /** 136 * Retrieve the styled string value for the attribute at <var>index</var>. 137 * 138 * @param index Index of attribute to retrieve. 139 * 140 * @return CharSequence holding string data. May be styled. Returns 141 * null if the attribute is not defined. 142 */ 143 @Override 144 public CharSequence getText(int index) { 145 if (index < 0 || index >= mResourceData.length) { 146 return null; 147 } 148 149 if (mResourceData[index] != null) { 150 // FIXME: handle styled strings! 151 return mResourceData[index].getValue(); 152 } 153 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 */ 165 @Override 166 public String getString(int index) { 167 if (index < 0 || index >= mResourceData.length) { 168 return null; 169 } 170 171 if (mResourceData[index] != null) { 172 return mResourceData[index].getValue(); 173 } 174 175 return null; 176 } 177 178 /** 179 * Retrieve the boolean value for the attribute at <var>index</var>. 180 * 181 * @param index Index of attribute to retrieve. 182 * @param defValue Value to return if the attribute is not defined. 183 * 184 * @return Attribute boolean value, or defValue if not defined. 185 */ 186 @Override 187 public boolean getBoolean(int index, boolean defValue) { 188 if (index < 0 || index >= mResourceData.length) { 189 return defValue; 190 } 191 192 if (mResourceData[index] == null) { 193 return defValue; 194 } 195 196 String s = mResourceData[index].getValue(); 197 if (s != null) { 198 return XmlUtils.convertValueToBoolean(s, defValue); 199 } 200 201 return defValue; 202 } 203 204 /** 205 * Retrieve the integer value for the attribute at <var>index</var>. 206 * 207 * @param index Index of attribute to retrieve. 208 * @param defValue Value to return if the attribute is not defined. 209 * 210 * @return Attribute int value, or defValue if not defined. 211 */ 212 @Override 213 public int getInt(int index, int defValue) { 214 if (index < 0 || index >= mResourceData.length) { 215 return defValue; 216 } 217 218 if (mResourceData[index] == null) { 219 return defValue; 220 } 221 222 String s = mResourceData[index].getValue(); 223 224 if (s == null || s.length() == 0) { 225 return defValue; 226 } 227 228 try { 229 return XmlUtils.convertValueToInt(s, defValue); 230 } catch (NumberFormatException e) { 231 // pass 232 } 233 234 // Field is not null and is not an integer. 235 // Check for possible constants and try to find them. 236 return (int) resolveEnumAttribute(index, defValue); 237 } 238 239 /** 240 * Searches for the string in the attributes (flag or enums) and returns the integer. 241 * If found, it will return an integer matching the value. However, if the value is not found, 242 * it returns {@code defValue} which may be a float. 243 * 244 * @param index Index of attribute to retrieve. 245 * @param defValue Value to return if the attribute is not found. 246 * 247 * @return Attribute int value, or defValue if not defined. 248 */ 249 private float resolveEnumAttribute(int index, float defValue) { 250 // Get the map of attribute-constant -> IntegerValue 251 Map<String, Integer> map = null; 252 if (mIsFramework[index]) { 253 map = Bridge.getEnumValues(mNames[index]); 254 } else { 255 // get the styleable matching the resolved name 256 RenderResources res = mContext.getRenderResources(); 257 ResourceValue attr = res.getProjectResource(ResourceType.ATTR, mNames[index]); 258 if (attr instanceof AttrResourceValue) { 259 map = ((AttrResourceValue) attr).getAttributeValues(); 260 } 261 } 262 263 if (map != null) { 264 // accumulator to store the value of the 1+ constants. 265 int result = 0; 266 267 // split the value in case this is a mix of several flags. 268 String[] keywords = mResourceData[index].getValue().split("\\|"); 269 for (String keyword : keywords) { 270 Integer i = map.get(keyword.trim()); 271 if (i != null) { 272 result |= i; 273 } else { 274 Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT, 275 String.format( 276 "\"%s\" in attribute \"%2$s\" is not a valid value", 277 keyword, mNames[index]), null); 278 } 279 } 280 return result; 281 } 282 283 return defValue; 284 } 285 286 /** 287 * Retrieve the float value for the attribute at <var>index</var>. 288 * 289 * @param index Index of attribute to retrieve. 290 * 291 * @return Attribute float value, or defValue if not defined.. 292 */ 293 @Override 294 public float getFloat(int index, float defValue) { 295 if (index < 0 || index >= mResourceData.length) { 296 return defValue; 297 } 298 299 if (mResourceData[index] == null) { 300 return defValue; 301 } 302 303 String s = mResourceData[index].getValue(); 304 305 if (s != null) { 306 try { 307 return Float.parseFloat(s); 308 } catch (NumberFormatException e) { 309 Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT, 310 String.format( 311 "\"%s\" in attribute \"%2$s\" cannot be converted to float.", 312 s, mNames[index]), null); 313 314 // we'll return the default value below. 315 } 316 } 317 return defValue; 318 } 319 320 /** 321 * Retrieve the color value for the attribute at <var>index</var>. If 322 * the attribute references a color resource holding a complex 323 * {@link android.content.res.ColorStateList}, then the default color from 324 * the set is returned. 325 * 326 * @param index Index of attribute to retrieve. 327 * @param defValue Value to return if the attribute is not defined or 328 * not a resource. 329 * 330 * @return Attribute color value, or defValue if not defined. 331 */ 332 @Override 333 public int getColor(int index, int defValue) { 334 if (index < 0 || index >= mResourceData.length) { 335 return defValue; 336 } 337 338 if (mResourceData[index] == null) { 339 return defValue; 340 } 341 342 ColorStateList colorStateList = ResourceHelper.getColorStateList( 343 mResourceData[index], mContext); 344 if (colorStateList != null) { 345 return colorStateList.getDefaultColor(); 346 } 347 348 return defValue; 349 } 350 351 /** 352 * Retrieve the ColorStateList for the attribute at <var>index</var>. 353 * The value may be either a single solid color or a reference to 354 * a color or complex {@link android.content.res.ColorStateList} description. 355 * 356 * @param index Index of attribute to retrieve. 357 * 358 * @return ColorStateList for the attribute, or null if not defined. 359 */ 360 @Override 361 public ColorStateList getColorStateList(int index) { 362 if (index < 0 || index >= mResourceData.length) { 363 return null; 364 } 365 366 if (mResourceData[index] == null) { 367 return null; 368 } 369 370 ResourceValue resValue = mResourceData[index]; 371 String value = resValue.getValue(); 372 373 if (value == null) { 374 return null; 375 } 376 377 // let the framework inflate the ColorStateList from the XML file. 378 File f = new File(value); 379 if (f.isFile()) { 380 try { 381 XmlPullParser parser = ParserFactory.create(f); 382 383 BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser( 384 parser, mContext, resValue.isFramework()); 385 try { 386 return ColorStateList.createFromXml(mContext.getResources(), blockParser); 387 } finally { 388 blockParser.ensurePopped(); 389 } 390 } catch (XmlPullParserException e) { 391 Bridge.getLog().error(LayoutLog.TAG_BROKEN, 392 "Failed to configure parser for " + value, e, null); 393 return null; 394 } catch (Exception e) { 395 // this is an error and not warning since the file existence is checked before 396 // attempting to parse it. 397 Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ, 398 "Failed to parse file " + value, e, null); 399 400 return null; 401 } 402 } 403 404 try { 405 int color = ResourceHelper.getColor(value); 406 return ColorStateList.valueOf(color); 407 } catch (NumberFormatException e) { 408 Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT, e.getMessage(), e, null); 409 } 410 411 return null; 412 } 413 414 /** 415 * Retrieve the integer value for the attribute at <var>index</var>. 416 * 417 * @param index Index of attribute to retrieve. 418 * @param defValue Value to return if the attribute is not defined or 419 * not a resource. 420 * 421 * @return Attribute integer value, or defValue if not defined. 422 */ 423 @Override 424 public int getInteger(int index, int defValue) { 425 return getInt(index, defValue); 426 } 427 428 /** 429 * Retrieve a dimensional unit attribute at <var>index</var>. Unit 430 * conversions are based on the current {@link DisplayMetrics} 431 * associated with the resources this {@link TypedArray} object 432 * came from. 433 * 434 * @param index Index of attribute to retrieve. 435 * @param defValue Value to return if the attribute is not defined or 436 * not a resource. 437 * 438 * @return Attribute dimension value multiplied by the appropriate 439 * metric, or defValue if not defined. 440 * 441 * @see #getDimensionPixelOffset 442 * @see #getDimensionPixelSize 443 */ 444 @Override 445 public float getDimension(int index, float defValue) { 446 if (index < 0 || index >= mResourceData.length) { 447 return defValue; 448 } 449 450 if (mResourceData[index] == null) { 451 return defValue; 452 } 453 454 String s = mResourceData[index].getValue(); 455 456 if (s == null) { 457 return defValue; 458 } 459 460 if (ResourceHelper.parseFloatAttribute(mNames[index], s, mValue, true)) { 461 return mValue.getDimension(mBridgeResources.getDisplayMetrics()); 462 } 463 464 // looks like we were unable to resolve the dimension value. Check if it is an attribute 465 // constant. 466 return resolveEnumAttribute(index, defValue); 467 } 468 469 /** 470 * Retrieve a dimensional unit attribute at <var>index</var> for use 471 * as an offset in raw pixels. This is the same as 472 * {@link #getDimension}, except the returned value is converted to 473 * integer pixels for you. An offset conversion involves simply 474 * truncating the base value to an integer. 475 * 476 * @param index Index of attribute to retrieve. 477 * @param defValue Value to return if the attribute is not defined or 478 * not a resource. 479 * 480 * @return Attribute dimension value multiplied by the appropriate 481 * metric and truncated to integer pixels, or defValue if not defined. 482 * 483 * @see #getDimension 484 * @see #getDimensionPixelSize 485 */ 486 @Override 487 public int getDimensionPixelOffset(int index, int defValue) { 488 return (int) getDimension(index, defValue); 489 } 490 491 /** 492 * Retrieve a dimensional unit attribute at <var>index</var> for use 493 * as a size in raw pixels. This is the same as 494 * {@link #getDimension}, except the returned value is converted to 495 * integer pixels for use as a size. A size conversion involves 496 * rounding the base value, and ensuring that a non-zero base value 497 * is at least one pixel in size. 498 * 499 * @param index Index of attribute to retrieve. 500 * @param defValue Value to return if the attribute is not defined or 501 * not a resource. 502 * 503 * @return Attribute dimension value multiplied by the appropriate 504 * metric and truncated to integer pixels, or defValue if not defined. 505 * 506 * @see #getDimension 507 * @see #getDimensionPixelOffset 508 */ 509 @Override 510 public int getDimensionPixelSize(int index, int defValue) { 511 try { 512 return getDimension(index); 513 } catch (RuntimeException e) { 514 if (mResourceData[index] != null) { 515 String s = mResourceData[index].getValue(); 516 517 if (s != null) { 518 // looks like we were unable to resolve the dimension value 519 Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT, 520 String.format( 521 "\"%1$s\" in attribute \"%2$s\" is not a valid format.", 522 s, mNames[index]), null); 523 } 524 } 525 526 return defValue; 527 } 528 } 529 530 /** 531 * Special version of {@link #getDimensionPixelSize} for retrieving 532 * {@link android.view.ViewGroup}'s layout_width and layout_height 533 * attributes. This is only here for performance reasons; applications 534 * should use {@link #getDimensionPixelSize}. 535 * 536 * @param index Index of the attribute to retrieve. 537 * @param name Textual name of attribute for error reporting. 538 * 539 * @return Attribute dimension value multiplied by the appropriate 540 * metric and truncated to integer pixels. 541 */ 542 @Override 543 public int getLayoutDimension(int index, String name) { 544 try { 545 // this will throw an exception 546 return getDimension(index); 547 } catch (RuntimeException e) { 548 549 if (LayoutInflater_Delegate.sIsInInclude) { 550 throw new RuntimeException(); 551 } 552 553 Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT, 554 "You must supply a " + name + " attribute.", null); 555 556 return 0; 557 } 558 } 559 560 @Override 561 public int getLayoutDimension(int index, int defValue) { 562 return getDimensionPixelSize(index, defValue); 563 } 564 565 private int getDimension(int index) { 566 if (mResourceData[index] == null) { 567 throw new RuntimeException(); 568 } 569 570 String s = mResourceData[index].getValue(); 571 572 if (s == null) { 573 throw new RuntimeException(); 574 } else if (s.equals(BridgeConstants.MATCH_PARENT) || 575 s.equals(BridgeConstants.FILL_PARENT)) { 576 return LayoutParams.MATCH_PARENT; 577 } else if (s.equals(BridgeConstants.WRAP_CONTENT)) { 578 return LayoutParams.WRAP_CONTENT; 579 } 580 581 if (ResourceHelper.parseFloatAttribute(mNames[index], s, mValue, true)) { 582 float f = mValue.getDimension(mBridgeResources.getDisplayMetrics()); 583 584 final int res = (int)(f+0.5f); 585 if (res != 0) return res; 586 if (f == 0) return 0; 587 if (f > 0) return 1; 588 } 589 590 throw new RuntimeException(); 591 } 592 593 /** 594 * Retrieve a fractional unit attribute at <var>index</var>. 595 * 596 * @param index Index of attribute to retrieve. 597 * @param base The base value of this fraction. In other words, a 598 * standard fraction is multiplied by this value. 599 * @param pbase The parent base value of this fraction. In other 600 * words, a parent fraction (nn%p) is multiplied by this 601 * value. 602 * @param defValue Value to return if the attribute is not defined or 603 * not a resource. 604 * 605 * @return Attribute fractional value multiplied by the appropriate 606 * base value, or defValue if not defined. 607 */ 608 @Override 609 public float getFraction(int index, int base, int pbase, float defValue) { 610 if (index < 0 || index >= mResourceData.length) { 611 return defValue; 612 } 613 614 if (mResourceData[index] == null) { 615 return defValue; 616 } 617 618 String value = mResourceData[index].getValue(); 619 if (value == null) { 620 return defValue; 621 } 622 623 if (ResourceHelper.parseFloatAttribute(mNames[index], value, mValue, false)) { 624 return mValue.getFraction(base, pbase); 625 } 626 627 // looks like we were unable to resolve the fraction value 628 Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT, 629 String.format( 630 "\"%1$s\" in attribute \"%2$s\" cannot be converted to a fraction.", 631 value, mNames[index]), null); 632 633 return defValue; 634 } 635 636 /** 637 * Retrieve the resource identifier for the attribute at 638 * <var>index</var>. Note that attribute resource as resolved when 639 * the overall {@link TypedArray} object is retrieved. As a 640 * result, this function will return the resource identifier of the 641 * final resource value that was found, <em>not</em> necessarily the 642 * original resource that was specified by the attribute. 643 * 644 * @param index Index of attribute to retrieve. 645 * @param defValue Value to return if the attribute is not defined or 646 * not a resource. 647 * 648 * @return Attribute resource identifier, or defValue if not defined. 649 */ 650 @Override 651 public int getResourceId(int index, int defValue) { 652 if (index < 0 || index >= mResourceData.length) { 653 return defValue; 654 } 655 656 // get the Resource for this index 657 ResourceValue resValue = mResourceData[index]; 658 659 // no data, return the default value. 660 if (resValue == null) { 661 return defValue; 662 } 663 664 // check if this is a style resource 665 if (resValue instanceof StyleResourceValue) { 666 // get the id that will represent this style. 667 return mContext.getDynamicIdByStyle((StyleResourceValue)resValue); 668 } 669 670 // if the attribute was a reference to a resource, and not a declaration of an id (@+id), 671 // then the xml attribute value was "resolved" which leads us to a ResourceValue with a 672 // valid getType() and getName() returning a resource name. 673 // (and getValue() returning null!). We need to handle this! 674 if (resValue.getResourceType() != null) { 675 // if this is a framework id 676 if (mPlatformFile || resValue.isFramework()) { 677 // look for idName in the android R classes 678 return mContext.getFrameworkResourceValue( 679 resValue.getResourceType(), resValue.getName(), defValue); 680 } 681 682 // look for idName in the project R class. 683 return mContext.getProjectResourceValue( 684 resValue.getResourceType(), resValue.getName(), defValue); 685 } 686 687 // else, try to get the value, and resolve it somehow. 688 String value = resValue.getValue(); 689 if (value == null) { 690 return defValue; 691 } 692 693 // if the value is just an integer, return it. 694 try { 695 int i = Integer.parseInt(value); 696 if (Integer.toString(i).equals(value)) { 697 return i; 698 } 699 } catch (NumberFormatException e) { 700 // pass 701 } 702 703 // Handle the @id/<name>, @+id/<name> and @android:id/<name> 704 // We need to return the exact value that was compiled (from the various R classes), 705 // as these values can be reused internally with calls to findViewById(). 706 // There's a trick with platform layouts that not use "android:" but their IDs are in 707 // fact in the android.R and com.android.internal.R classes. 708 // The field mPlatformFile will indicate that all IDs are to be looked up in the android R 709 // classes exclusively. 710 711 // if this is a reference to an id, find it. 712 if (value.startsWith("@id/") || value.startsWith("@+") || 713 value.startsWith("@android:id/")) { 714 715 int pos = value.indexOf('/'); 716 String idName = value.substring(pos + 1); 717 718 // if this is a framework id 719 if (mPlatformFile || value.startsWith("@android") || value.startsWith("@+android")) { 720 // look for idName in the android R classes 721 return mContext.getFrameworkResourceValue(ResourceType.ID, idName, defValue); 722 } 723 724 // look for idName in the project R class. 725 return mContext.getProjectResourceValue(ResourceType.ID, idName, defValue); 726 } 727 728 // not a direct id valid reference? resolve it 729 Integer idValue; 730 731 if (resValue.isFramework()) { 732 idValue = Bridge.getResourceId(resValue.getResourceType(), 733 resValue.getName()); 734 } else { 735 idValue = mContext.getProjectCallback().getResourceId( 736 resValue.getResourceType(), resValue.getName()); 737 } 738 739 if (idValue != null) { 740 return idValue; 741 } 742 743 Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_RESOLVE, 744 String.format( 745 "Unable to resolve id \"%1$s\" for attribute \"%2$s\"", value, mNames[index]), 746 resValue); 747 748 return defValue; 749 } 750 751 @Override 752 public int getThemeAttributeId(int index, int defValue) { 753 // TODO: Get the right Theme Attribute ID to enable caching of the drawables. 754 return defValue; 755 } 756 757 /** 758 * Retrieve the Drawable for the attribute at <var>index</var>. This 759 * gets the resource ID of the selected attribute, and uses 760 * {@link Resources#getDrawable Resources.getDrawable} of the owning 761 * Resources object to retrieve its Drawable. 762 * 763 * @param index Index of attribute to retrieve. 764 * 765 * @return Drawable for the attribute, or null if not defined. 766 */ 767 @Override 768 public Drawable getDrawable(int index) { 769 if (index < 0 || index >= mResourceData.length) { 770 return null; 771 } 772 773 if (mResourceData[index] == null) { 774 return null; 775 } 776 777 ResourceValue value = mResourceData[index]; 778 String stringValue = value.getValue(); 779 if (stringValue == null) { 780 return null; 781 } 782 783 return ResourceHelper.getDrawable(value, mContext); 784 } 785 786 787 /** 788 * Retrieve the CharSequence[] for the attribute at <var>index</var>. 789 * This gets the resource ID of the selected attribute, and uses 790 * {@link Resources#getTextArray Resources.getTextArray} of the owning 791 * Resources object to retrieve its String[]. 792 * 793 * @param index Index of attribute to retrieve. 794 * 795 * @return CharSequence[] for the attribute, or null if not defined. 796 */ 797 @Override 798 public CharSequence[] getTextArray(int index) { 799 if (index < 0 || index >= mResourceData.length) { 800 return null; 801 } 802 803 if (mResourceData[index] == null) { 804 return null; 805 } 806 807 String value = mResourceData[index].getValue(); 808 if (value != null) { 809 return new CharSequence[] { value }; 810 } 811 812 Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT, 813 String.format( 814 String.format("Unknown value for getTextArray(%d) => %s", //DEBUG 815 index, mResourceData[index].getName())), null); 816 817 return null; 818 } 819 820 @Override 821 public int[] extractThemeAttrs() { 822 // The drawables are always inflated with a Theme and we don't care about caching. So, 823 // just return. 824 return null; 825 } 826 827 @Override 828 public int getChangingConfigurations() { 829 // We don't care about caching. Any change in configuration is a fresh render. So, 830 // just return. 831 return 0; 832 } 833 834 /** 835 * Retrieve the raw TypedValue for the attribute at <var>index</var>. 836 * 837 * @param index Index of attribute to retrieve. 838 * @param outValue TypedValue object in which to place the attribute's 839 * data. 840 * 841 * @return Returns true if the value was retrieved, else false. 842 */ 843 @Override 844 public boolean getValue(int index, TypedValue outValue) { 845 if (index < 0 || index >= mResourceData.length) { 846 return false; 847 } 848 849 if (mResourceData[index] == null) { 850 return false; 851 } 852 853 String s = mResourceData[index].getValue(); 854 855 return ResourceHelper.parseFloatAttribute(mNames[index], s, outValue, false); 856 } 857 858 /** 859 * Determines whether there is an attribute at <var>index</var>. 860 * 861 * @param index Index of attribute to retrieve. 862 * 863 * @return True if the attribute has a value, false otherwise. 864 */ 865 @Override 866 public boolean hasValue(int index) { 867 //noinspection SimplifiableIfStatement 868 if (index < 0 || index >= mResourceData.length) { 869 return false; 870 } 871 872 return mResourceData[index] != null; 873 } 874 875 /** 876 * Retrieve the raw TypedValue for the attribute at <var>index</var> 877 * and return a temporary object holding its data. This object is only 878 * valid until the next call on to {@link TypedArray}. 879 * 880 * @param index Index of attribute to retrieve. 881 * 882 * @return Returns a TypedValue object if the attribute is defined, 883 * containing its data; otherwise returns null. (You will not 884 * receive a TypedValue whose type is TYPE_NULL.) 885 */ 886 @Override 887 public TypedValue peekValue(int index) { 888 if (index < 0 || index >= mResourceData.length) { 889 return null; 890 } 891 892 if (getValue(index, mValue)) { 893 return mValue; 894 } 895 896 return null; 897 } 898 899 /** 900 * Returns a message about the parser state suitable for printing error messages. 901 */ 902 @Override 903 public String getPositionDescription() { 904 return "<internal -- stub if needed>"; 905 } 906 907 /** 908 * Give back a previously retrieved TypedArray, for later re-use. 909 */ 910 @Override 911 public void recycle() { 912 // pass 913 } 914 915 @Override 916 public String toString() { 917 return Arrays.toString(mResourceData); 918 } 919 920 static TypedArray obtain(Resources res, int len) { 921 return res instanceof BridgeResources ? 922 new BridgeTypedArray(((BridgeResources) res), null, len, true) : null; 923 } 924} 925