diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java index 2c2c6723f981f..e5b47d64fc3b0 100644 --- a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java +++ b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java @@ -16,6 +16,8 @@ package android.content.res; +import com.android.SdkConstants; +import com.android.ide.common.rendering.api.ArrayResourceValue; import com.android.ide.common.rendering.api.LayoutLog; import com.android.ide.common.rendering.api.LayoutlibCallback; import com.android.ide.common.rendering.api.ResourceValue; @@ -32,6 +34,8 @@ import com.android.util.Pair; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.DisplayMetrics; @@ -42,6 +46,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; +import java.util.Iterator; /** * @@ -241,6 +246,145 @@ public final class BridgeResources extends Resources { return null; } + @Override + public CharSequence[] getTextArray(int id) throws NotFoundException { + ResourceValue resValue = getArrayResourceValue(id); + if (resValue == null) { + // Error already logged by getArrayResourceValue. + return new CharSequence[0]; + } else if (!(resValue instanceof ArrayResourceValue)) { + return new CharSequence[]{ + resolveReference(resValue.getValue(), resValue.isFramework())}; + } + ArrayResourceValue arv = ((ArrayResourceValue) resValue); + return fillValues(arv, new CharSequence[arv.getElementCount()]); + } + + @Override + public String[] getStringArray(int id) throws NotFoundException { + ResourceValue resValue = getArrayResourceValue(id); + if (resValue == null) { + // Error already logged by getArrayResourceValue. + return new String[0]; + } else if (!(resValue instanceof ArrayResourceValue)) { + return new String[]{ + resolveReference(resValue.getValue(), resValue.isFramework())}; + } + ArrayResourceValue arv = ((ArrayResourceValue) resValue); + return fillValues(arv, new String[arv.getElementCount()]); + } + + /** + * Resolve each element in resValue and copy them to {@code values}. The values copied are + * always Strings. The ideal signature for the method should be <T super String>, but java + * generics don't support it. + */ + private T[] fillValues(ArrayResourceValue resValue, T[] values) { + int i = 0; + for (Iterator iterator = resValue.iterator(); iterator.hasNext(); i++) { + @SuppressWarnings("unchecked") + T s = (T) resolveReference(iterator.next(), resValue.isFramework()); + values[i] = s; + } + return values; + } + + @Override + public int[] getIntArray(int id) throws NotFoundException { + ResourceValue rv = getArrayResourceValue(id); + if (rv == null) { + // Error already logged by getArrayResourceValue. + return new int[0]; + } else if (!(rv instanceof ArrayResourceValue)) { + // This is an older IDE that can only give us the first element of the array. + String firstValue = resolveReference(rv.getValue(), rv.isFramework()); + try { + return new int[]{getInt(firstValue)}; + } catch (NumberFormatException e) { + Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT, + "Integer resource array contains non-integer value: " + + firstValue, null); + return new int[1]; + } + } + ArrayResourceValue resValue = ((ArrayResourceValue) rv); + int[] values = new int[resValue.getElementCount()]; + int i = 0; + for (Iterator iterator = resValue.iterator(); iterator.hasNext(); i++) { + String element = resolveReference(iterator.next(), resValue.isFramework()); + try { + values[i] = getInt(element); + } catch (NumberFormatException e) { + Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT, + "Integer resource array contains non-integer value: " + element, null); + } + } + return values; + } + + /** + * Try to find the ArrayResourceValue for the given id. + *

+ * If the ResourceValue found is not of type {@link ResourceType#ARRAY}, the method logs an + * error and return null. However, if the ResourceValue found has type {@code + * ResourceType.ARRAY}, but the value is not an instance of {@link ArrayResourceValue}, the + * method returns the ResourceValue. This happens on older versions of the IDE, which did not + * parse the array resources properly. + *

+ * @throws NotFoundException if no resource if found + */ + @Nullable + private ResourceValue getArrayResourceValue(int id) throws NotFoundException { + Pair v = getResourceValue(id, mPlatformResourceFlag); + + if (v != null) { + ResourceValue resValue = v.getSecond(); + + assert resValue != null; + if (resValue != null) { + final ResourceType type = resValue.getResourceType(); + if (type != ResourceType.ARRAY) { + Bridge.getLog().error(LayoutLog.TAG_RESOURCES_RESOLVE, + String.format( + "Resource with id 0x%1$X is not an array resource, but %2$s", + id, type == null ? "null" : type.getDisplayName()), + null); + return null; + } + if (!(resValue instanceof ArrayResourceValue)) { + Bridge.getLog().warning(LayoutLog.TAG_UNSUPPORTED, + "Obtaining resource arrays via getTextArray, getStringArray or getIntArray is not fully supported in this version of the IDE.", + null); + } + return resValue; + } + } + + // id was not found or not resolved. Throw a NotFoundException. + throwException(id); + + // this is not used since the method above always throws + return null; + } + + @NonNull + private String resolveReference(@NonNull String ref, boolean forceFrameworkOnly) { + if (ref.startsWith(SdkConstants.PREFIX_RESOURCE_REF) || ref.startsWith + (SdkConstants.PREFIX_THEME_REF)) { + ResourceValue rv = + mContext.getRenderResources().findResValue(ref, forceFrameworkOnly); + rv = mContext.getRenderResources().resolveResValue(rv); + if (rv != null) { + return rv.getValue(); + } else { + Bridge.getLog().error(LayoutLog.TAG_RESOURCES_RESOLVE, + "Unable to resolve resource " + ref, null); + } + } + // Not a reference. + return ref; + } + @Override public XmlResourceParser getLayout(int id) throws NotFoundException { Pair v = getResourceValue(id, mPlatformResourceFlag); @@ -431,13 +575,8 @@ public final class BridgeResources extends Resources { if (resValue != null) { String v = resValue.getValue(); if (v != null) { - int radix = 10; - if (v.startsWith("0x")) { - v = v.substring(2); - radix = 16; - } try { - return Integer.parseInt(v, radix); + return getInt(v); } catch (NumberFormatException e) { // return exception below } @@ -610,7 +749,6 @@ public final class BridgeResources extends Resources { } } - @Override public InputStream openRawResource(int id) throws NotFoundException { Pair value = getResourceValue(id, mPlatformResourceFlag); @@ -691,7 +829,7 @@ public final class BridgeResources extends Resources { resourceInfo = mLayoutlibCallback.resolveResourceId(id); } - String message = null; + String message; if (resourceInfo != null) { message = String.format( "Could not find %1$s resource matching value 0x%2$X (resolved name: %3$s) in current configuration.", @@ -703,4 +841,15 @@ public final class BridgeResources extends Resources { throw new NotFoundException(message); } + + private int getInt(String v) throws NumberFormatException { + int radix = 10; + if (v.startsWith("0x")) { + v = v.substring(2); + radix = 16; + } else if (v.startsWith("0")) { + radix = 8; + } + return Integer.parseInt(v, radix); + } } diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build.gradle b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build.gradle index 0f37fce20104b..4561e1b801250 100644 --- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build.gradle +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build.gradle @@ -3,7 +3,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:1.1.3' + classpath 'com.android.tools.build:gradle:1.2.3' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -19,12 +19,12 @@ allprojects { apply plugin: 'com.android.application' android { - compileSdkVersion 21 + compileSdkVersion 22 buildToolsVersion '21.1.2' defaultConfig { applicationId 'com.android.layoutlib.test.myapplication' - minSdkVersion 19 - targetSdkVersion 21 + minSdkVersion 21 + targetSdkVersion 22 versionCode 1 versionName '1.0' } diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/ArraysCheckWidget.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/ArraysCheckWidget.class new file mode 100644 index 0000000000000..e0373cba84a7c Binary files /dev/null and b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/ArraysCheckWidget.class differ diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class index 8af93ebda1e42..ec42017bad67e 100644 Binary files a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class and b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class differ diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$array.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$array.class new file mode 100644 index 0000000000000..b87f193c68a0a Binary files /dev/null and b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$array.class differ diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$attr.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$attr.class index 9bab801635bf2..e2968d40f7184 100644 Binary files a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$attr.class and b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$attr.class differ diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$dimen.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$dimen.class index 7ad860522790a..0e208f2dc7def 100644 Binary files a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$dimen.class and b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$dimen.class differ diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$drawable.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$drawable.class index e9e0a3341ee2e..2b77af367a385 100644 Binary files a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$drawable.class and b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$drawable.class differ diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.class index 069f9f7e826bf..fd01b445df0db 100644 Binary files a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.class and b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.class differ diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$integer.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$integer.class new file mode 100644 index 0000000000000..91cf5b6d824be Binary files /dev/null and b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$integer.class differ diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class index 14997511584bd..e172b2d9d5717 100644 Binary files a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class and b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class differ diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class index ca438add633e4..aecbff6add3bc 100644 Binary files a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class and b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class differ diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class index a98abf57306ae..fc3f23600d86d 100644 Binary files a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class and b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class differ diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class index 7d8cc84643349..83ad35bd0ab19 100644 Binary files a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class and b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class differ diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class index 7e6113b3aa11c..d5b81c48b4043 100644 Binary files a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class and b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class differ diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/array_check.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/array_check.png new file mode 100644 index 0000000000000..9a135684f33e1 Binary files /dev/null and b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/array_check.png differ diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/ArraysCheckWidget.java b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/ArraysCheckWidget.java new file mode 100644 index 0000000000000..41d75de7ada77 --- /dev/null +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/ArraysCheckWidget.java @@ -0,0 +1,41 @@ +package com.android.layoutlib.test.myapplication; + +import android.content.Context; +import android.content.res.Resources; +import android.util.AttributeSet; +import android.widget.LinearLayout; +import android.widget.TextView; + +/** + * A widget to test obtaining arrays from resources. + */ +public class ArraysCheckWidget extends LinearLayout { + public ArraysCheckWidget(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public ArraysCheckWidget(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public ArraysCheckWidget(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + Resources resources = context.getResources(); + for (CharSequence chars : resources.getTextArray(R.array.array)) { + addTextView(context, chars); + } + for (int i : resources.getIntArray(R.array.int_array)) { + addTextView(context, String.valueOf(i)); + } + for (String string : resources.getStringArray(R.array.string_array)) { + addTextView(context, string); + } + } + + private void addTextView(Context context, CharSequence string) { + TextView textView = new TextView(context); + textView.setText(string); + textView.setTextSize(30); + addView(textView); + } +} diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/array_check.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/array_check.xml new file mode 100644 index 0000000000000..50646ab4c388f --- /dev/null +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/array_check.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/arrays.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/arrays.xml new file mode 100644 index 0000000000000..f6e14d2b83b9e --- /dev/null +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/arrays.xml @@ -0,0 +1,25 @@ + + + + first + second + + + 1 + 0xaB + 010 + @integer/ten + ?attr/myattr + + + mystring + @string/hello_world + + ?android:attr/candidatesTextStyleSpans + @android:string/unknownName + + + + 10 + 12 + \ No newline at end of file diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/attrs.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/attrs.xml new file mode 100644 index 0000000000000..894e18cfe48bb --- /dev/null +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/attrs.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml index ff6c9d2c0fb9e..88c9cbcb250aa 100644 --- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml @@ -1,7 +1,8 @@ - diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java index 9394253322ee7..b9a2e31793f3a 100644 --- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java +++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java @@ -296,6 +296,11 @@ public class Main { renderAndVerify("allwidgets.xml", "allwidgets.png"); } + @Test + public void testArrayCheck() throws ClassNotFoundException { + renderAndVerify("array_check.xml", "array_check.png"); + } + /** Test expand_layout.xml */ @Test public void testExpand() throws ClassNotFoundException { @@ -388,7 +393,6 @@ public class Main { ResourceResolver.create(mProjectResources.getConfiguredResources(config), mFrameworkRepo.getConfiguredResources(config), themeName, false); - return new SessionParams( layoutParser, renderingMode,