* limitations under the License.
*/
-package com.android.layoutlib.bridge;
+package com.android.layoutlib.bridge.android;
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.RenderResources;
+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.api.IResourceValue;
-import com.android.layoutlib.api.IStyleResourceValue;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.BridgeConstants;
+import com.android.layoutlib.bridge.impl.ResourceHelper;
+import com.android.resources.ResourceType;
import org.kxml2.io.KXmlParser;
import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.util.DisplayMetrics;
import android.util.TypedValue;
+import android.view.LayoutInflater_Delegate;
import android.view.ViewGroup.LayoutParams;
import java.io.File;
-import java.io.FileReader;
+import java.io.FileInputStream;
+import java.util.Arrays;
import java.util.Map;
/**
- * TODO: describe.
+ * Custom implementation of TypedArray to handle non compiled resources.
*/
public final class BridgeTypedArray extends TypedArray {
- @SuppressWarnings("hiding")
- private BridgeResources mResources;
+ private BridgeResources mBridgeResources;
private BridgeContext mContext;
- @SuppressWarnings("hiding")
- private IResourceValue[] mData;
+ private ResourceValue[] mResourceData;
private String[] mNames;
private final boolean mPlatformFile;
public BridgeTypedArray(BridgeResources resources, BridgeContext context, int len,
boolean platformFile) {
super(null, null, null, 0);
- mResources = resources;
+ mBridgeResources = resources;
mContext = context;
mPlatformFile = platformFile;
- mData = new IResourceValue[len];
+ mResourceData = new ResourceValue[len];
mNames = new String[len];
}
/** A bridge-specific method that sets a value in the type array */
- public void bridgeSetValue(int index, String name, IResourceValue value) {
- mData[index] = value;
+ public void bridgeSetValue(int index, String name, ResourceValue value) {
+ mResourceData[index] = value;
mNames[index] = name;
}
/**
- * Seals the array after all calls to {@link #bridgeSetValue(int, String, IResourceValue)} have
+ * Seals the array after all calls to {@link #bridgeSetValue(int, String, ResourceValue)} have
* been done.
* <p/>This allows to compute the list of non default values, permitting
* {@link #getIndexCount()} to return the proper value.
// fills TypedArray.mIndices which is used to implement getIndexCount/getIndexAt
// first count the array size
int count = 0;
- for (IResourceValue data : mData) {
+ for (ResourceValue data : mResourceData) {
if (data != null) {
count++;
}
// fill the array with the indices.
int index = 1;
- for (int i = 0 ; i < mData.length ; i++) {
- if (mData[i] != null) {
+ for (int i = 0 ; i < mResourceData.length ; i++) {
+ if (mResourceData[i] != null) {
mIndices[index++] = i;
}
}
*/
@Override
public int length() {
- return mData.length;
+ return mResourceData.length;
}
/**
*/
@Override
public Resources getResources() {
- return mResources;
+ return mBridgeResources;
}
/**
*/
@Override
public CharSequence getText(int index) {
- if (mData[index] != null) {
+ if (mResourceData[index] != null) {
// FIXME: handle styled strings!
- return mData[index].getValue();
+ return mResourceData[index].getValue();
}
return null;
*/
@Override
public String getString(int index) {
- if (mData[index] != null) {
- return mData[index].getValue();
+ if (mResourceData[index] != null) {
+ return mResourceData[index].getValue();
}
return null;
*/
@Override
public boolean getBoolean(int index, boolean defValue) {
- if (mData[index] == null) {
+ if (mResourceData[index] == null) {
return defValue;
}
- String s = mData[index].getValue();
+ String s = mResourceData[index].getValue();
if (s != null) {
return XmlUtils.convertValueToBoolean(s, defValue);
}
*/
@Override
public int getInt(int index, int defValue) {
- if (mData[index] == null) {
+ if (mResourceData[index] == null) {
return defValue;
}
- String s = mData[index].getValue();
+ String s = mResourceData[index].getValue();
+
+ if (RenderResources.REFERENCE_NULL.equals(s)) {
+ return defValue;
+ }
try {
return (s == null) ? defValue : XmlUtils.convertValueToInt(s, defValue);
if (i != null) {
result |= i.intValue();
} else {
- mContext.getLogger().warning(String.format(
- "Unknown constant \"%s\" in attribute \"%2$s\"",
- keyword, mNames[index]));
+ Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+ String.format(
+ "\"%s\" in attribute \"%2$s\" is not a valid value",
+ keyword, mNames[index]), null /*data*/);
}
}
return result;
*/
@Override
public float getFloat(int index, float defValue) {
- if (mData[index] == null) {
+ if (mResourceData[index] == null) {
return defValue;
}
- String s = mData[index].getValue();
+ String s = mResourceData[index].getValue();
if (s != null) {
try {
return Float.parseFloat(s);
} catch (NumberFormatException e) {
- mContext.getLogger().warning(String.format(
- "Unable to convert \"%s\" into a float in attribute \"%2$s\"",
- s, mNames[index]));
+ Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+ String.format(
+ "\"%s\" in attribute \"%2$s\" cannot be converted to float.",
+ s, mNames[index]), null /*data*/);
// we'll return the default value below.
}
*/
@Override
public int getColor(int index, int defValue) {
- if (mData[index] == null) {
+ if (mResourceData[index] == null) {
return defValue;
}
- String s = mData[index].getValue();
- try {
- return ResourceHelper.getColor(s);
- } catch (NumberFormatException e) {
- mContext.getLogger().warning(String.format(
- "Unable to convert \"%s\" into a color in attribute \"%2$s\"",
- s, mNames[index]));
-
- // we'll return the default value below.
+ ColorStateList colorStateList = ResourceHelper.getColorStateList(
+ mResourceData[index], mContext);
+ if (colorStateList != null) {
+ return colorStateList.getDefaultColor();
}
return defValue;
*/
@Override
public ColorStateList getColorStateList(int index) {
- if (mData[index] == null) {
+ if (mResourceData[index] == null) {
return null;
}
- String value = mData[index].getValue();
+ ResourceValue resValue = mResourceData[index];
+ String value = resValue.getValue();
if (value == null) {
return null;
}
- try {
- int color = ResourceHelper.getColor(value);
- return ColorStateList.valueOf(color);
- } catch (NumberFormatException e) {
- // if it's not a color value, we'll attempt to read the xml based color below.
+ if (RenderResources.REFERENCE_NULL.equals(value)) {
+ return null;
}
// let the framework inflate the ColorStateList from the XML file.
- try {
- File f = new File(value);
- if (f.isFile()) {
+ File f = new File(value);
+ if (f.isFile()) {
+ try {
KXmlParser parser = new KXmlParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
- parser.setInput(new FileReader(f));
-
- ColorStateList colorStateList = ColorStateList.createFromXml(
- mContext.getResources(),
- // FIXME: we need to know if this resource is platform or not
- new BridgeXmlBlockParser(parser, mContext, false));
- return colorStateList;
+ parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$);
+
+ BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
+ parser, mContext, resValue.isFramework());
+ try {
+ return ColorStateList.createFromXml(mContext.getResources(), blockParser);
+ } finally {
+ blockParser.ensurePopped();
+ }
+ } catch (XmlPullParserException e) {
+ Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+ "Failed to configure parser for " + value, e, null /*data*/);
+ return null;
+ } catch (Exception e) {
+ // this is an error and not warning since the file existence is checked before
+ // attempting to parse it.
+ Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
+ "Failed to parse file " + value, e, null /*data*/);
+
+ return null;
}
- } catch (Exception e) {
- // this is an error and not warning since the file existence is checked before
- // attempting to parse it.
- mContext.getLogger().error(e);
-
- // return null below.
}
- // looks like were unable to resolve the color value.
- mContext.getLogger().warning(String.format(
- "Unable to resolve color value \"%1$s\" in attribute \"%2$s\"",
- value, mNames[index]));
+ try {
+ int color = ResourceHelper.getColor(value);
+ return ColorStateList.valueOf(color);
+ } catch (NumberFormatException e) {
+ Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT, e.getMessage(), e, null /*data*/);
+ }
return null;
}
*/
@Override
public int getInteger(int index, int defValue) {
- if (mData[index] == null) {
+ if (mResourceData[index] == null) {
return defValue;
}
- String s = mData[index].getValue();
+ String s = mResourceData[index].getValue();
if (s != null) {
try {
return Integer.parseInt(s);
} catch (NumberFormatException e) {
- mContext.getLogger().warning(String.format(
- "Unable to convert \"%s\" into a integer in attribute \"%2$s\"",
- s, mNames[index]));
+ Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+ String.format(
+ "\"%s\" in attribute \"%2$s\" cannont be converted to an integer.",
+ s, mNames[index]), null /*data*/);
// The default value is returned below.
}
*/
@Override
public float getDimension(int index, float defValue) {
- if (mData[index] == null) {
+ if (mResourceData[index] == null) {
return defValue;
}
- String s = mData[index].getValue();
+ String s = mResourceData[index].getValue();
if (s == null) {
return defValue;
return LayoutParams.MATCH_PARENT;
} else if (s.equals(BridgeConstants.WRAP_CONTENT)) {
return LayoutParams.WRAP_CONTENT;
+ } else if (RenderResources.REFERENCE_NULL.equals(s)) {
+ return defValue;
}
if (ResourceHelper.stringToFloat(s, mValue)) {
- return mValue.getDimension(mResources.mMetrics);
+ return mValue.getDimension(mBridgeResources.mMetrics);
}
// looks like we were unable to resolve the dimension value
- mContext.getLogger().warning(String.format(
- "Unable to resolve dimension value \"%1$s\" in attribute \"%2$s\"",
- s, mNames[index]));
+ Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+ String.format(
+ "\"%1$s\" in attribute \"%2$s\" is not a valid format.",
+ s, mNames[index]), null /*data*/);
return defValue;
}
*/
@Override
public int getDimensionPixelSize(int index, int defValue) {
- if (mData[index] == null) {
- return defValue;
- }
-
- String s = mData[index].getValue();
+ try {
+ return getDimension(index);
+ } catch (RuntimeException e) {
+ if (mResourceData[index] != null) {
+ String s = mResourceData[index].getValue();
+
+ 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 /*data*/);
+ }
+ }
- if (s == null) {
return defValue;
- } 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;
}
-
- // FIXME huh?
-
- float f = getDimension(index, defValue);
- final int res = (int)(f+0.5f);
- if (res != 0) return res;
- if (f == 0) return 0;
- if (f > 0) return 1;
-
- throw new UnsupportedOperationException("Can't convert to dimension: " +
- Integer.toString(index));
}
/**
*/
@Override
public int getLayoutDimension(int index, String name) {
- return getDimensionPixelSize(index, 0);
+ try {
+ // this will throw an exception
+ return getDimension(index);
+ } catch (RuntimeException e) {
+
+ if (LayoutInflater_Delegate.sIsInInclude) {
+ throw new RuntimeException();
+ }
+
+ Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+ "You must supply a " + name + " attribute.", null);
+
+ return 0;
+ }
}
@Override
return getDimensionPixelSize(index, defValue);
}
+ private int getDimension(int index) {
+ if (mResourceData[index] == null) {
+ throw new RuntimeException();
+ }
+
+ String s = mResourceData[index].getValue();
+
+ 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;
+ } else if (RenderResources.REFERENCE_NULL.equals(s)) {
+ throw new RuntimeException();
+ }
+
+ if (ResourceHelper.stringToFloat(s, mValue)) {
+ float f = mValue.getDimension(mBridgeResources.mMetrics);
+
+ final int res = (int)(f+0.5f);
+ if (res != 0) return res;
+ if (f == 0) return 0;
+ if (f > 0) return 1;
+ }
+
+ throw new RuntimeException();
+ }
+
/**
* Retrieve a fractional unit attribute at <var>index</var>.
*
*/
@Override
public float getFraction(int index, int base, int pbase, float defValue) {
- if (mData[index] == null) {
+ if (mResourceData[index] == null) {
return defValue;
}
- String value = mData[index].getValue();
+ String value = mResourceData[index].getValue();
if (value == null) {
return defValue;
}
}
// looks like we were unable to resolve the fraction value
- mContext.getLogger().warning(String.format(
- "Unable to resolve fraction value \"%1$s\" in attribute \"%2$s\"",
- value, mNames[index]));
+ Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+ String.format(
+ "\"%1$s\" in attribute \"%2$s\" cannont be converted to a fraction.",
+ value, mNames[index]), null /*data*/);
return defValue;
}
*/
@Override
public int getResourceId(int index, int defValue) {
- // get the IResource for this index
- IResourceValue resValue = mData[index];
+ // get the Resource for this index
+ ResourceValue resValue = mResourceData[index];
// no data, return the default value.
if (resValue == null) {
}
// check if this is a style resource
- if (resValue instanceof IStyleResourceValue) {
+ if (resValue instanceof StyleResourceValue) {
// get the id that will represent this style.
- return mContext.getDynamicIdByStyle((IStyleResourceValue)resValue);
+ return mContext.getDynamicIdByStyle((StyleResourceValue)resValue);
+ }
+
+ if (RenderResources.REFERENCE_NULL.equals(resValue.getValue())) {
+ return defValue;
}
- // if the attribute was a reference to an id, and not a declaration of an id (@+id), then
- // the xml attribute value was "resolved" which leads us to a IResourceValue with
- // getType() returning "id" and getName() returning the id name
+ // if the attribute was a reference to a resource, and not a declaration of an id (@+id),
+ // then the xml attribute value was "resolved" which leads us to a ResourceValue with a
+ // valid getType() and getName() returning a resource name.
// (and getValue() returning null!). We need to handle this!
- if (resValue.getType() != null && resValue.getType().equals(BridgeConstants.RES_ID)) {
+ if (resValue.getResourceType() != null) {
// if this is a framework id
if (mPlatformFile || resValue.isFramework()) {
// look for idName in the android R classes
- return mContext.getFrameworkIdValue(resValue.getName(), defValue);
+ return mContext.getFrameworkResourceValue(
+ resValue.getResourceType(), resValue.getName(), defValue);
}
// look for idName in the project R class.
- return mContext.getProjectIdValue(resValue.getName(), defValue);
+ return mContext.getProjectResourceValue(
+ resValue.getResourceType(), resValue.getName(), defValue);
}
// else, try to get the value, and resolve it somehow.
// if this is a framework id
if (mPlatformFile || value.startsWith("@android") || value.startsWith("@+android")) {
// look for idName in the android R classes
- return mContext.getFrameworkIdValue(idName, defValue);
+ return mContext.getFrameworkResourceValue(ResourceType.ID, idName, defValue);
}
// look for idName in the project R class.
- return mContext.getProjectIdValue(idName, defValue);
+ return mContext.getProjectResourceValue(ResourceType.ID, idName, defValue);
}
// not a direct id valid reference? resolve it
Integer idValue = null;
if (resValue.isFramework()) {
- idValue = Bridge.getResourceValue(resValue.getType(), resValue.getName());
+ idValue = Bridge.getResourceId(resValue.getResourceType(),
+ resValue.getName());
} else {
- idValue = mContext.getProjectCallback().getResourceValue(
- resValue.getType(), resValue.getName());
+ idValue = mContext.getProjectCallback().getResourceId(
+ resValue.getResourceType(), resValue.getName());
}
if (idValue != null) {
return idValue.intValue();
}
- mContext.getLogger().warning(String.format(
- "Unable to resolve id \"%1$s\" for attribute \"%2$s\"", value, mNames[index]));
+ Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_RESOLVE,
+ String.format(
+ "Unable to resolve id \"%1$s\" for attribute \"%2$s\"", value, mNames[index]),
+ resValue);
+
return defValue;
}
*/
@Override
public Drawable getDrawable(int index) {
- if (mData[index] == null) {
+ if (mResourceData[index] == null) {
return null;
}
- IResourceValue value = mData[index];
+ ResourceValue value = mResourceData[index];
String stringValue = value.getValue();
- if (stringValue == null || BridgeConstants.REFERENCE_NULL.equals(stringValue)) {
+ if (stringValue == null || RenderResources.REFERENCE_NULL.equals(stringValue)) {
return null;
}
- Drawable d = ResourceHelper.getDrawable(value, mContext, mData[index].isFramework());
-
- if (d != null) {
- return d;
- }
-
- // looks like we were unable to resolve the drawable
- mContext.getLogger().warning(String.format(
- "Unable to resolve drawable \"%1$s\" in attribute \"%2$s\"", stringValue,
- mNames[index]));
-
- return null;
+ return ResourceHelper.getDrawable(value, mContext);
}
*/
@Override
public CharSequence[] getTextArray(int index) {
- if (mData[index] == null) {
+ if (mResourceData[index] == null) {
return null;
}
- String value = mData[index].getValue();
+ String value = mResourceData[index].getValue();
if (value != null) {
+ if (RenderResources.REFERENCE_NULL.equals(value)) {
+ return null;
+ }
+
return new CharSequence[] { value };
}
- mContext.getLogger().warning(String.format(
- String.format("Unknown value for getTextArray(%d) => %s", //DEBUG
- index, mData[index].getName())));
+ Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+ String.format(
+ String.format("Unknown value for getTextArray(%d) => %s", //DEBUG
+ index, mResourceData[index].getName())), null /*data*/);
return null;
}
*/
@Override
public boolean getValue(int index, TypedValue outValue) {
- if (mData[index] == null) {
+ if (mResourceData[index] == null) {
return false;
}
- String s = mData[index].getValue();
+ String s = mResourceData[index].getValue();
return ResourceHelper.stringToFloat(s, outValue);
}
*/
@Override
public boolean hasValue(int index) {
- return mData[index] != null;
+ return mResourceData[index] != null;
}
/**
@Override
public String toString() {
- return mData.toString();
+ return Arrays.toString(mResourceData);
}
}