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