Always resolve enum when resolving resources.

Enums were resolved only for integers and dimensions. This change
resolves enums for all resource types. Well, almost all. For color and
colorStateList, enums are still not referenced.

Bug: http://b.android.com/76091
Change-Id: Ie43bd1b54fb9877655d31773bdf71d9a6a65c473
This commit is contained in:
Deepanshu Gupta
2014-09-29 09:32:42 -07:00
parent 8d5a15b691
commit 171804201d
2 changed files with 109 additions and 201 deletions

View File

@@ -23,7 +23,6 @@ import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.rendering.api.StyleResourceValue;
import com.android.internal.util.XmlUtils;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.BridgeConstants;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
import com.android.layoutlib.bridge.impl.ParserFactory;
@@ -142,16 +141,8 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public CharSequence getText(int index) {
if (index < 0 || index >= mResourceData.length) {
return null;
}
if (mResourceData[index] != null) {
// FIXME: handle styled strings!
return mResourceData[index].getValue();
}
return null;
// FIXME: handle styled strings!
return getString(index);
}
/**
@@ -164,15 +155,14 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public String getString(int index) {
if (index < 0 || index >= mResourceData.length) {
if (!hasValue(index)) {
return null;
}
if (mResourceData[index] != null) {
return mResourceData[index].getValue();
}
return null;
// As unfortunate as it is, it's possible to use enums with all attribute formats,
// not just integers/enums. So, we need to search the enums always. In case,
// enums are used, the returned value is an integer.
Integer v = resolveEnumAttribute(index);
return v == null ? mResourceData[index].getValue() : String.valueOf((int) v);
}
/**
@@ -185,20 +175,9 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public boolean getBoolean(int index, boolean defValue) {
if (index < 0 || index >= mResourceData.length) {
return defValue;
}
String s = getString(index);
return s == null ? defValue : XmlUtils.convertValueToBoolean(s, defValue);
if (mResourceData[index] == null) {
return defValue;
}
String s = mResourceData[index].getValue();
if (s != null) {
return XmlUtils.convertValueToBoolean(s, defValue);
}
return defValue;
}
/**
@@ -211,75 +190,18 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public int getInt(int index, int defValue) {
if (index < 0 || index >= mResourceData.length) {
return defValue;
}
if (mResourceData[index] == null) {
return defValue;
}
String s = mResourceData[index].getValue();
if (s == null || s.length() == 0) {
return defValue;
}
String s = getString(index);
try {
return XmlUtils.convertValueToInt(s, defValue);
if (s != null) {
return XmlUtils.convertValueToInt(s, defValue);
}
} catch (NumberFormatException e) {
// pass
Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
String.format("\"%s\" in attribute \"%2$s\" is not a valid integer",
s, mNames[index]),
null);
return defValue;
}
// Field is not null and is not an integer.
// Check for possible constants and try to find them.
return (int) resolveEnumAttribute(index, defValue);
}
/**
* Searches for the string in the attributes (flag or enums) and returns the integer.
* If found, it will return an integer matching the value. However, if the value is not found,
* it returns {@code defValue} which may be a float.
*
* @param index Index of attribute to retrieve.
* @param defValue Value to return if the attribute is not found.
*
* @return Attribute int value, or defValue if not defined.
*/
private float resolveEnumAttribute(int index, float defValue) {
// Get the map of attribute-constant -> IntegerValue
Map<String, Integer> map = null;
if (mIsFramework[index]) {
map = Bridge.getEnumValues(mNames[index]);
} else {
// get the styleable matching the resolved name
RenderResources res = mContext.getRenderResources();
ResourceValue attr = res.getProjectResource(ResourceType.ATTR, mNames[index]);
if (attr instanceof AttrResourceValue) {
map = ((AttrResourceValue) attr).getAttributeValues();
}
}
if (map != null) {
// accumulator to store the value of the 1+ constants.
int result = 0;
// split the value in case this is a mix of several flags.
String[] keywords = mResourceData[index].getValue().split("\\|");
for (String keyword : keywords) {
Integer i = map.get(keyword.trim());
if (i != null) {
result |= i;
} else {
Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
String.format(
"\"%s\" in attribute \"%2$s\" is not a valid value",
keyword, mNames[index]), null);
}
}
return result;
}
return defValue;
}
@@ -292,27 +214,16 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public float getFloat(int index, float defValue) {
if (index < 0 || index >= mResourceData.length) {
return defValue;
}
if (mResourceData[index] == null) {
return defValue;
}
String s = mResourceData[index].getValue();
if (s != null) {
try {
return Float.parseFloat(s);
} catch (NumberFormatException e) {
Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
String.format(
"\"%s\" in attribute \"%2$s\" cannot be converted to float.",
s, mNames[index]), null);
// we'll return the default value below.
String s = getString(index);
try {
if (s != null) {
return Float.parseFloat(s);
}
} catch (NumberFormatException e) {
Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
String.format("\"%s\" in attribute \"%2$s\" cannot be converted to float.",
s, mNames[index]),
null);
}
return defValue;
}
@@ -359,11 +270,7 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public ColorStateList getColorStateList(int index) {
if (index < 0 || index >= mResourceData.length) {
return null;
}
if (mResourceData[index] == null) {
if (!hasValue(index)) {
return null;
}
@@ -443,27 +350,25 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public float getDimension(int index, float defValue) {
if (index < 0 || index >= mResourceData.length) {
return defValue;
}
if (mResourceData[index] == null) {
return defValue;
}
String s = mResourceData[index].getValue();
String s = getString(index);
if (s == null) {
return defValue;
}
// Check if the value is a magic constant that doesn't require a unit.
try {
int i = Integer.parseInt(s);
if (i == LayoutParams.MATCH_PARENT || i == LayoutParams.WRAP_CONTENT) {
return i;
}
} catch (NumberFormatException ignored) {
// pass
}
if (ResourceHelper.parseFloatAttribute(mNames[index], s, mValue, true)) {
return mValue.getDimension(mBridgeResources.getDisplayMetrics());
}
// looks like we were unable to resolve the dimension value. Check if it is an attribute
// constant.
return resolveEnumAttribute(index, defValue);
return defValue;
}
/**
@@ -511,16 +416,13 @@ public final class BridgeTypedArray extends TypedArray {
try {
return getDimension(index);
} catch (RuntimeException e) {
if (mResourceData[index] != null) {
String s = mResourceData[index].getValue();
String s = getString(index);
if (s != null) {
// looks like we were unable to resolve the dimension value
Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
String.format(
"\"%1$s\" in attribute \"%2$s\" is not a valid format.",
if (s != null) {
// looks like we were unable to resolve the dimension value
Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
String.format("\"%1$s\" in attribute \"%2$s\" is not a valid format.",
s, mNames[index]), null);
}
}
return defValue;
@@ -563,21 +465,19 @@ public final class BridgeTypedArray extends TypedArray {
}
private int getDimension(int index) {
if (mResourceData[index] == null) {
throw new RuntimeException();
}
String s = mResourceData[index].getValue();
String s = getString(index);
if (s == null) {
throw new RuntimeException();
} else if (s.equals(BridgeConstants.MATCH_PARENT) ||
s.equals(BridgeConstants.FILL_PARENT)) {
return LayoutParams.MATCH_PARENT;
} else if (s.equals(BridgeConstants.WRAP_CONTENT)) {
return LayoutParams.WRAP_CONTENT;
}
// Check if the value is a magic constant that doesn't require a unit.
try {
int i = Integer.parseInt(s);
if (i == LayoutParams.MATCH_PARENT || i == LayoutParams.WRAP_CONTENT) {
return i;
}
} catch (NumberFormatException ignored) {
// pass
}
if (ResourceHelper.parseFloatAttribute(mNames[index], s, mValue, true)) {
float f = mValue.getDimension(mBridgeResources.getDisplayMetrics());
@@ -607,15 +507,7 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public float getFraction(int index, int base, int pbase, float defValue) {
if (index < 0 || index >= mResourceData.length) {
return defValue;
}
if (mResourceData[index] == null) {
return defValue;
}
String value = mResourceData[index].getValue();
String value = getString(index);
if (value == null) {
return defValue;
}
@@ -766,20 +658,11 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public Drawable getDrawable(int index) {
if (index < 0 || index >= mResourceData.length) {
return null;
}
if (mResourceData[index] == null) {
if (!hasValue(index)) {
return null;
}
ResourceValue value = mResourceData[index];
String stringValue = value.getValue();
if (stringValue == null) {
return null;
}
return ResourceHelper.getDrawable(value, mContext);
}
@@ -796,15 +679,7 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public CharSequence[] getTextArray(int index) {
if (index < 0 || index >= mResourceData.length) {
return null;
}
if (mResourceData[index] == null) {
return null;
}
String value = mResourceData[index].getValue();
String value = getString(index);
if (value != null) {
return new CharSequence[] { value };
}
@@ -837,17 +712,8 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public boolean getValue(int index, TypedValue outValue) {
if (index < 0 || index >= mResourceData.length) {
return false;
}
if (mResourceData[index] == null) {
return false;
}
String s = mResourceData[index].getValue();
return ResourceHelper.parseFloatAttribute(mNames[index], s, outValue, false);
String s = getString(index);
return s != null && ResourceHelper.parseFloatAttribute(mNames[index], s, outValue, false);
}
/**
@@ -859,12 +725,7 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public boolean hasValue(int index) {
//noinspection SimplifiableIfStatement
if (index < 0 || index >= mResourceData.length) {
return false;
}
return mResourceData[index] != null;
return index >= 0 && index < mResourceData.length && mResourceData[index] != null;
}
/**
@@ -912,6 +773,52 @@ public final class BridgeTypedArray extends TypedArray {
return Arrays.toString(mResourceData);
}
/**
* Searches for the string in the attributes (flag or enums) and returns the integer.
* If found, it will return an integer matching the value.
*
* @param index Index of attribute to retrieve.
*
* @return Attribute int value, or null if not defined.
*/
private Integer resolveEnumAttribute(int index) {
// Get the map of attribute-constant -> IntegerValue
Map<String, Integer> map = null;
if (mIsFramework[index]) {
map = Bridge.getEnumValues(mNames[index]);
} else {
// get the styleable matching the resolved name
RenderResources res = mContext.getRenderResources();
ResourceValue attr = res.getProjectResource(ResourceType.ATTR, mNames[index]);
if (attr instanceof AttrResourceValue) {
map = ((AttrResourceValue) attr).getAttributeValues();
}
}
if (map != null) {
// accumulator to store the value of the 1+ constants.
int result = 0;
boolean found = false;
// split the value in case this is a mix of several flags.
String[] keywords = mResourceData[index].getValue().split("\\|");
for (String keyword : keywords) {
Integer i = map.get(keyword.trim());
if (i != null) {
result |= i;
found = true;
}
// TODO: We should act smartly and log a warning for incorrect keywords. However,
// this method is currently called even if the resourceValue is not an enum.
}
if (found) {
return result;
}
}
return null;
}
static TypedArray obtain(Resources res, int len) {
return res instanceof BridgeResources ?
new BridgeTypedArray(((BridgeResources) res), null, len, true) : null;

View File

@@ -16,6 +16,7 @@
package com.android.layoutlib.bridge.impl;
import com.android.annotations.NonNull;
import com.android.ide.common.rendering.api.DensityBasedResourceValue;
import com.android.ide.common.rendering.api.LayoutLog;
import com.android.ide.common.rendering.api.RenderResources;
@@ -358,7 +359,7 @@ public final class ResourceHelper {
* @param requireUnit whether the value is expected to contain a unit.
* @return true if success.
*/
public static boolean parseFloatAttribute(String attribute, String value,
public static boolean parseFloatAttribute(String attribute, @NonNull String value,
TypedValue outValue, boolean requireUnit) {
assert !requireUnit || attribute != null;