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