diff --git a/tools/layoutlib/.idea/libraries/mockito.xml b/tools/layoutlib/.idea/libraries/mockito.xml
new file mode 100644
index 0000000000000..032963e40b940
--- /dev/null
+++ b/tools/layoutlib/.idea/libraries/mockito.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tools/layoutlib/.idea/libraries/objenesis.xml b/tools/layoutlib/.idea/libraries/objenesis.xml
new file mode 100644
index 0000000000000..1484de59be6a7
--- /dev/null
+++ b/tools/layoutlib/.idea/libraries/objenesis.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tools/layoutlib/bridge/bridge.iml b/tools/layoutlib/bridge/bridge.iml
index fbaed520fff90..85ec3eb73b923 100644
--- a/tools/layoutlib/bridge/bridge.iml
+++ b/tools/layoutlib/bridge/bridge.iml
@@ -86,5 +86,7 @@
+
+
\ No newline at end of file
diff --git a/tools/layoutlib/bridge/src/android/util/BridgeXmlPullAttributes.java b/tools/layoutlib/bridge/src/android/util/BridgeXmlPullAttributes.java
index 138b2d5fca3d0..9fd1e1557537c 100644
--- a/tools/layoutlib/bridge/src/android/util/BridgeXmlPullAttributes.java
+++ b/tools/layoutlib/bridge/src/android/util/BridgeXmlPullAttributes.java
@@ -16,6 +16,7 @@
package android.util;
+import com.android.ide.common.rendering.api.AttrResourceValue;
import com.android.ide.common.rendering.api.RenderResources;
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.internal.util.XmlUtils;
@@ -27,6 +28,11 @@ import com.android.resources.ResourceType;
import org.xmlpull.v1.XmlPullParser;
+import android.annotation.NonNull;
+
+import java.util.Map;
+import java.util.function.Function;
+
/**
* A correct implementation of the {@link AttributeSet} interface on top of a XmlPullParser
*/
@@ -34,12 +40,30 @@ public class BridgeXmlPullAttributes extends XmlPullAttributes {
private final BridgeContext mContext;
private final boolean mPlatformFile;
+ private final Function> mFrameworkEnumValueSupplier;
+ private final Function> mProjectEnumValueSupplier;
- public BridgeXmlPullAttributes(XmlPullParser parser, BridgeContext context,
- boolean platformFile) {
+ // VisibleForTesting
+ BridgeXmlPullAttributes(@NonNull XmlPullParser parser, @NonNull BridgeContext context,
+ boolean platformFile,
+ @NonNull Function> frameworkEnumValueSupplier,
+ @NonNull Function> projectEnumValueSupplier) {
super(parser);
mContext = context;
mPlatformFile = platformFile;
+ mFrameworkEnumValueSupplier = frameworkEnumValueSupplier;
+ mProjectEnumValueSupplier = projectEnumValueSupplier;
+ }
+
+ public BridgeXmlPullAttributes(@NonNull XmlPullParser parser, @NonNull BridgeContext context,
+ boolean platformFile) {
+ this(parser, context, platformFile, Bridge::getEnumValues, attrName -> {
+ // get the styleable matching the resolved name
+ RenderResources res = context.getRenderResources();
+ ResourceValue attr = res.getProjectResource(ResourceType.ATTR, attrName);
+ return attr instanceof AttrResourceValue ?
+ ((AttrResourceValue) attr).getAttributeValues() : null;
+ });
}
/*
@@ -59,12 +83,8 @@ public class BridgeXmlPullAttributes extends XmlPullAttributes {
String ns = mParser.getAttributeNamespace(index);
if (BridgeConstants.NS_RESOURCES.equals(ns)) {
- Integer v = Bridge.getResourceId(ResourceType.ATTR, name);
- if (v != null) {
- return v.intValue();
- }
+ return Bridge.getResourceId(ResourceType.ATTR, name);
- return 0;
}
// this is not an attribute in the android namespace, we query the customviewloader, if
@@ -72,7 +92,7 @@ public class BridgeXmlPullAttributes extends XmlPullAttributes {
if (mContext.getLayoutlibCallback().getNamespace().equals(ns)) {
Integer v = mContext.getLayoutlibCallback().getResourceId(ResourceType.ATTR, name);
if (v != null) {
- return v.intValue();
+ return v;
}
}
@@ -121,20 +141,38 @@ public class BridgeXmlPullAttributes extends XmlPullAttributes {
}
@Override
- public int getAttributeIntValue(String namespace, String attribute,
- int defaultValue) {
+ public int getAttributeIntValue(String namespace, String attribute, int defaultValue) {
String value = getAttributeValue(namespace, attribute);
- if (value != null) {
- ResourceValue r = getResourceValue(value);
-
- if (r != null) {
- value = r.getValue();
- }
-
- return XmlUtils.convertValueToInt(value, defaultValue);
+ if (value == null) {
+ return defaultValue;
}
- return defaultValue;
+ ResourceValue r = getResourceValue(value);
+
+ if (r != null) {
+ value = r.getValue();
+ }
+
+ if (value.charAt(0) == '#') {
+ return ResourceHelper.getColor(value);
+ }
+
+ try {
+ return XmlUtils.convertValueToInt(value, defaultValue);
+ } catch (NumberFormatException e) {
+ // This is probably an enum
+ Map enumValues = BridgeConstants.NS_RESOURCES.equals(namespace) ?
+ mFrameworkEnumValueSupplier.apply(attribute) :
+ mProjectEnumValueSupplier.apply(attribute);
+
+ Integer enumValue = enumValues != null ? enumValues.get(value) : null;
+ if (enumValue != null) {
+ return enumValue;
+ }
+
+ // We weren't able to find the enum int value
+ throw e;
+ }
}
@Override
@@ -203,21 +241,9 @@ public class BridgeXmlPullAttributes extends XmlPullAttributes {
@Override
public int getAttributeIntValue(int index, int defaultValue) {
- String value = getAttributeValue(index);
- if (value != null) {
- ResourceValue r = getResourceValue(value);
-
- if (r != null) {
- value = r.getValue();
- }
-
- if (value.charAt(0) == '#') {
- return ResourceHelper.getColor(value);
- }
- return XmlUtils.convertValueToInt(value, defaultValue);
- }
-
- return defaultValue;
+ return getAttributeIntValue(mParser.getAttributeNamespace(index),
+ getAttributeName(index)
+ , defaultValue);
}
@Override
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index dff4f6905db55..c9b04dcf8ed42 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -111,7 +111,7 @@ import static com.android.layoutlib.bridge.android.RenderParamsFlags.FLAG_KEY_AP
* Custom implementation of Context/Activity to handle non compiled resources.
*/
@SuppressWarnings("deprecation") // For use of Pair.
-public final class BridgeContext extends Context {
+public class BridgeContext extends Context {
private static final String PREFIX_THEME_APPCOMPAT = "Theme.AppCompat";
private static final Map FRAMEWORK_PATCHED_VALUES = new HashMap<>(2);
diff --git a/tools/layoutlib/bridge/tests/Android.mk b/tools/layoutlib/bridge/tests/Android.mk
index 33d55dea6f79c..1b65eee729e65 100644
--- a/tools/layoutlib/bridge/tests/Android.mk
+++ b/tools/layoutlib/bridge/tests/Android.mk
@@ -31,7 +31,8 @@ LOCAL_JAVA_LIBRARIES := layoutlib \
tools-common-prebuilt \
sdk-common \
junit-host \
- guavalib
+ guavalib \
+ mockito-host
include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tools/layoutlib/bridge/tests/src/android/util/BridgeXmlPullAttributesTest.java b/tools/layoutlib/bridge/tests/src/android/util/BridgeXmlPullAttributesTest.java
new file mode 100644
index 0000000000000..2fcec8e98585e
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/src/android/util/BridgeXmlPullAttributesTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import com.android.ide.common.rendering.api.RenderResources;
+import com.android.layoutlib.bridge.BridgeConstants;
+import com.android.layoutlib.bridge.android.BridgeContext;
+
+import org.junit.Test;
+import org.xmlpull.v1.XmlPullParser;
+
+import com.google.common.collect.ImmutableMap;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class BridgeXmlPullAttributesTest {
+ @Test
+ public void testGetAttributeIntValueForEnums() {
+ RenderResources renderResources = new RenderResources();
+
+ XmlPullParser parser = mock(XmlPullParser.class);
+ when(parser.getAttributeValue(BridgeConstants.NS_RESOURCES, "layout_width"))
+ .thenReturn("match_parent");
+ when(parser.getAttributeName(0)).thenReturn("layout_width");
+ when(parser.getAttributeNamespace(0)).thenReturn(BridgeConstants.NS_RESOURCES);
+ // Return every value twice since there is one test using name and other using index
+ when(parser.getAttributeValue("http://custom", "my_custom_attr"))
+ .thenReturn("a", "a", "b", "b", "invalid", "invalid");
+ when(parser.getAttributeName(1)).thenReturn("my_custom_attr");
+ when(parser.getAttributeNamespace(1)).thenReturn("http://custom");
+
+ BridgeContext context = mock(BridgeContext.class);
+ when(context.getRenderResources()).thenReturn(renderResources);
+
+ BridgeXmlPullAttributes attributes = new BridgeXmlPullAttributes(
+ parser,
+ context,
+ false,
+ attrName -> {
+ if ("layout_width".equals(attrName)) {
+ return ImmutableMap.of(
+ "match_parent", 123);
+ }
+ return ImmutableMap.of();
+ },
+ attrName -> {
+ if ("my_custom_attr".equals(attrName)) {
+ return ImmutableMap.of(
+ "a", 1,
+ "b", 2
+ );
+ }
+ return ImmutableMap.of();
+ });
+
+ // Test a framework defined enum attribute
+ assertEquals(123, attributes.getAttributeIntValue(BridgeConstants.NS_RESOURCES,
+ "layout_width", 500));
+ assertEquals(123, attributes.getAttributeIntValue(0, 500));
+ // Test non existing attribute (it should return the default value)
+ assertEquals(500, attributes.getAttributeIntValue(BridgeConstants.NS_RESOURCES,
+ "layout_height", 500));
+ assertEquals(500, attributes.getAttributeIntValue(2, 500));
+
+ // Test project defined enum attribute
+ assertEquals(1, attributes.getAttributeIntValue("http://custom",
+ "my_custom_attr", 500));
+ assertEquals(1, attributes.getAttributeIntValue(1, 500));
+ assertEquals(2, attributes.getAttributeIntValue("http://custom",
+ "my_custom_attr", 500));
+ assertEquals(2, attributes.getAttributeIntValue(1, 500));
+ // Test an invalid enum
+ boolean exception = false;
+ try {
+ attributes.getAttributeIntValue("http://custom", "my_custom_attr", 500);
+ } catch(NumberFormatException e) {
+ exception = true;
+ }
+ assertTrue(exception);
+ exception = false;
+ try {
+ attributes.getAttributeIntValue(1, 500);
+ } catch(NumberFormatException e) {
+ exception = true;
+ }
+ assertTrue(exception);
+
+ // Test non existing project attribute
+ assertEquals(500, attributes.getAttributeIntValue("http://custom",
+ "my_other_attr", 500));
+ }
+
+}
\ No newline at end of file