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