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