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