From 9b137e27d34efb89024543ffd2def2b37307aa0c Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Fri, 4 Mar 2016 19:31:28 +0530 Subject: [PATCH] Change BridgeResources to Resources_Delegate [DO NOT MERGE] Do not merge because the resource implementation has changed in N and the change is no longer valid there. This is a very hacky fix to make layoutlib work with the latest support lib. Inject a couple of fields in android.content.res.Resources to allow using most of the earlier code as is. Bug: 27403642 Bug: http://b.android.com/201934 Change-Id: I186cad32b1b4de64164fbad937d989e0110c6976 --- .../android/content/res/BridgeTypedArray.java | 10 +- ...Resources.java => Resources_Delegate.java} | 441 ++++++++++-------- .../graphics/BitmapFactory_Delegate.java | 2 +- .../bridge/android/BridgeContext.java | 12 +- .../bridge/util/NinePatchInputStream.java | 50 ++ .../tools/layoutlib/create/AsmGenerator.java | 5 + .../tools/layoutlib/create/CreateInfo.java | 27 +- .../create/FieldInjectorAdapter.java | 40 ++ 8 files changed, 367 insertions(+), 220 deletions(-) rename tools/layoutlib/bridge/src/android/content/res/{BridgeResources.java => Resources_Delegate.java} (63%) create mode 100644 tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java create mode 100644 tools/layoutlib/create/src/com/android/tools/layoutlib/create/FieldInjectorAdapter.java diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java index db4c6dc68b77f..c8f1b80742e8c 100644 --- a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java +++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java @@ -71,7 +71,7 @@ import static com.android.ide.common.rendering.api.RenderResources.REFERENCE_UND */ public final class BridgeTypedArray extends TypedArray { - private final BridgeResources mBridgeResources; + private final Resources mBridgeResources; private final BridgeContext mContext; private final boolean mPlatformFile; @@ -84,7 +84,7 @@ public final class BridgeTypedArray extends TypedArray { @Nullable private int[] mEmptyIds; - public BridgeTypedArray(BridgeResources resources, BridgeContext context, int len, + public BridgeTypedArray(Resources resources, BridgeContext context, int len, boolean platformFile) { super(resources, null, null, 0); mBridgeResources = resources; @@ -756,7 +756,8 @@ public final class BridgeTypedArray extends TypedArray { if (resVal instanceof ArrayResourceValue) { ArrayResourceValue array = (ArrayResourceValue) resVal; int count = array.getElementCount(); - return count >= 0 ? mBridgeResources.fillValues(array, new CharSequence[count]) : null; + return count >= 0 ? Resources_Delegate.fillValues(mBridgeResources, array, new CharSequence[count]) : + null; } int id = getResourceId(index, 0); String resIdMessage = id > 0 ? " (resource id 0x" + Integer.toHexString(id) + ')' : ""; @@ -1004,7 +1005,6 @@ public final class BridgeTypedArray extends TypedArray { } static TypedArray obtain(Resources res, int len) { - return res instanceof BridgeResources ? - new BridgeTypedArray(((BridgeResources) res), null, len, true) : null; + return new BridgeTypedArray(res, null, len, true); } } diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java b/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java similarity index 63% rename from tools/layoutlib/bridge/src/android/content/res/BridgeResources.java rename to tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java index 0e392436d7486..faf79aba82986 100644 --- a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java +++ b/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2016 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. @@ -28,8 +28,10 @@ import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; import com.android.layoutlib.bridge.impl.ParserFactory; import com.android.layoutlib.bridge.impl.ResourceHelper; +import com.android.layoutlib.bridge.util.NinePatchInputStream; import com.android.ninepatch.NinePatch; import com.android.resources.ResourceType; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import com.android.util.Pair; import org.xmlpull.v1.XmlPullParser; @@ -37,6 +39,8 @@ import org.xmlpull.v1.XmlPullParserException; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.res.Resources.NotFoundException; +import android.content.res.Resources.Theme; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.DisplayMetrics; @@ -49,86 +53,39 @@ import java.io.FileNotFoundException; import java.io.InputStream; import java.util.Iterator; -public final class BridgeResources extends Resources { +@SuppressWarnings("deprecation") +public class Resources_Delegate { - private BridgeContext mContext; - private LayoutlibCallback mLayoutlibCallback; - private boolean[] mPlatformResourceFlag = new boolean[1]; - private TypedValue mTmpValue = new TypedValue(); + private static boolean[] mPlatformResourceFlag = new boolean[1]; - /** - * Simpler wrapper around FileInputStream. This is used when the input stream represent - * not a normal bitmap but a nine patch. - * This is useful when the InputStream is created in a method but used in another that needs - * to know whether this is 9-patch or not, such as BitmapFactory. - */ - public class NinePatchInputStream extends FileInputStream { - private boolean mFakeMarkSupport = true; - public NinePatchInputStream(File file) throws FileNotFoundException { - super(file); - } - - @Override - public boolean markSupported() { - if (mFakeMarkSupport) { - // this is needed so that BitmapFactory doesn't wrap this in a BufferedInputStream. - return true; - } - - return super.markSupported(); - } - - public void disableFakeMarkSupport() { - // disable fake mark support so that in case codec actually try to use them - // we don't lie to them. - mFakeMarkSupport = false; - } - } - - /** - * This initializes the static field {@link Resources#mSystem} which is used - * by methods who get global resources using {@link Resources#getSystem()}. - *

- * They will end up using our bridge resources. - *

- * {@link Bridge} calls this method after setting up a new bridge. - */ public static Resources initSystem(BridgeContext context, AssetManager assets, DisplayMetrics metrics, Configuration config, LayoutlibCallback layoutlibCallback) { - return Resources.mSystem = new BridgeResources(context, - assets, - metrics, - config, - layoutlibCallback); + Resources resources = new Resources(assets, metrics, config); + resources.mContext = context; + resources.mLayoutlibCallback = layoutlibCallback; + return Resources.mSystem = resources; } /** - * Disposes the static {@link Resources#mSystem} to make sure we don't leave objects - * around that would prevent us from unloading the library. + * Disposes the static {@link Resources#mSystem} to make sure we don't leave objects around that + * would prevent us from unloading the library. */ public static void disposeSystem() { - if (Resources.mSystem instanceof BridgeResources) { - ((BridgeResources)(Resources.mSystem)).mContext = null; - ((BridgeResources)(Resources.mSystem)).mLayoutlibCallback = null; - } + Resources.mSystem.mContext = null; + Resources.mSystem.mLayoutlibCallback = null; Resources.mSystem = null; } - private BridgeResources(BridgeContext context, AssetManager assets, DisplayMetrics metrics, - Configuration config, LayoutlibCallback layoutlibCallback) { - super(assets, metrics, config); - mContext = context; - mLayoutlibCallback = layoutlibCallback; + public static BridgeTypedArray newTypeArray(Resources resources, int numEntries, + boolean platformFile) { + return new BridgeTypedArray(resources, resources.mContext, numEntries, platformFile); } - public BridgeTypedArray newTypeArray(int numEntries, boolean platformFile) { - return new BridgeTypedArray(this, mContext, numEntries, platformFile); - } - - private Pair getResourceValue(int id, boolean[] platformResFlag_out) { + private static Pair getResourceValue(Resources resources, int id, + boolean[] platformResFlag_out) { // first get the String related to this id in the framework Pair resourceInfo = Bridge.resolveResourceId(id); @@ -136,44 +93,62 @@ public final class BridgeResources extends Resources { platformResFlag_out[0] = true; String attributeName = resourceInfo.getSecond(); - return Pair.of(attributeName, mContext.getRenderResources().getFrameworkResource( - resourceInfo.getFirst(), attributeName)); + return Pair.of(attributeName, + resources.mContext.getRenderResources().getFrameworkResource( + resourceInfo.getFirst(), attributeName)); + } + + // Set the layoutlib callback and context for resources + if (resources != Resources.mSystem && resources.mLayoutlibCallback == null) { + resources.mLayoutlibCallback = Resources.mSystem.mLayoutlibCallback; + resources.mContext = Resources.mSystem.mContext; } // didn't find a match in the framework? look in the project. - if (mLayoutlibCallback != null) { - resourceInfo = mLayoutlibCallback.resolveResourceId(id); + if (resources.mLayoutlibCallback != null) { + resourceInfo = resources.mLayoutlibCallback.resolveResourceId(id); if (resourceInfo != null) { platformResFlag_out[0] = false; String attributeName = resourceInfo.getSecond(); - return Pair.of(attributeName, mContext.getRenderResources().getProjectResource( - resourceInfo.getFirst(), attributeName)); + return Pair.of(attributeName, + resources.mContext.getRenderResources().getProjectResource( + resourceInfo.getFirst(), attributeName)); } } return null; } - @Override - public Drawable getDrawable(int id, Theme theme) { - Pair value = getResourceValue(id, mPlatformResourceFlag); + @LayoutlibDelegate + static Drawable getDrawable(Resources resources, int id) { + return getDrawable(resources, id, null); + } + + @LayoutlibDelegate + static Drawable getDrawable(Resources resources, int id, Theme theme) { + Pair value = getResourceValue(resources, id, mPlatformResourceFlag); if (value != null) { - return ResourceHelper.getDrawable(value.getSecond(), mContext, theme); + return ResourceHelper.getDrawable(value.getSecond(), resources.mContext, theme); } // id was not found or not resolved. Throw a NotFoundException. - throwException(id); + throwException(resources, id); // this is not used since the method above always throws return null; } - @Override - public int getColor(int id, Theme theme) throws NotFoundException { - Pair value = getResourceValue(id, mPlatformResourceFlag); + @LayoutlibDelegate + static int getColor(Resources resources, int id) { + return getColor(resources, id, null); + } + + @LayoutlibDelegate + static int getColor(Resources resources, int id, Theme theme) throws NotFoundException { + Pair value = getResourceValue(resources, id, mPlatformResourceFlag); if (value != null) { ResourceValue resourceValue = value.getSecond(); @@ -185,7 +160,7 @@ public final class BridgeResources extends Resources { String message; if (new File(resourceValue.getValue()).isFile()) { String resource = (resourceValue.isFramework() ? "@android:" : "@") + "color/" - + resourceValue.getName(); + + resourceValue.getName(); message = "Hexadecimal color expected, found Color State List for " + resource; } else { message = e.getMessage(); @@ -198,31 +173,57 @@ public final class BridgeResources extends Resources { // Suppress possible NPE. getColorStateList will never return null, it will instead // throw an exception, but intelliJ can't figure that out //noinspection ConstantConditions - return getColorStateList(id, theme).getDefaultColor(); + return getColorStateList(resources, id, theme).getDefaultColor(); } - @Override - public ColorStateList getColorStateList(int id, Theme theme) throws NotFoundException { - Pair resValue = getResourceValue(id, mPlatformResourceFlag); + @LayoutlibDelegate + static ColorStateList getColorStateList(Resources resources, int id) throws NotFoundException { + return getColorStateList(resources, id, null); + } + + @LayoutlibDelegate + static ColorStateList getColorStateList(Resources resources, int id, Theme theme) + throws NotFoundException { + Pair resValue = + getResourceValue(resources, id, mPlatformResourceFlag); if (resValue != null) { ColorStateList stateList = ResourceHelper.getColorStateList(resValue.getSecond(), - mContext); + resources.mContext); if (stateList != null) { return stateList.obtainForTheme(theme); } } // id was not found or not resolved. Throw a NotFoundException. - throwException(id); + throwException(resources, id); // this is not used since the method above always throws return null; } - @Override - public CharSequence getText(int id) throws NotFoundException { - Pair value = getResourceValue(id, mPlatformResourceFlag); + @LayoutlibDelegate + static CharSequence getText(Resources resources, int id, CharSequence def) { + Pair value = getResourceValue(resources, id, mPlatformResourceFlag); + + if (value != null) { + ResourceValue resValue = value.getSecond(); + + assert resValue != null; + if (resValue != null) { + String v = resValue.getValue(); + if (v != null) { + return v; + } + } + } + + return def; + } + + @LayoutlibDelegate + static CharSequence getText(Resources resources, int id) throws NotFoundException { + Pair value = getResourceValue(resources, id, mPlatformResourceFlag); if (value != null) { ResourceValue resValue = value.getSecond(); @@ -237,38 +238,38 @@ public final class BridgeResources extends Resources { } // id was not found or not resolved. Throw a NotFoundException. - throwException(id); + throwException(resources, id); // this is not used since the method above always throws return null; } - @Override - public CharSequence[] getTextArray(int id) throws NotFoundException { - ResourceValue resValue = getArrayResourceValue(id); + @LayoutlibDelegate + static CharSequence[] getTextArray(Resources resources, int id) throws NotFoundException { + ResourceValue resValue = getArrayResourceValue(resources, 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())}; + resolveReference(resources, resValue.getValue(), resValue.isFramework())}; } ArrayResourceValue arv = ((ArrayResourceValue) resValue); - return fillValues(arv, new CharSequence[arv.getElementCount()]); + return fillValues(resources, arv, new CharSequence[arv.getElementCount()]); } - @Override - public String[] getStringArray(int id) throws NotFoundException { - ResourceValue resValue = getArrayResourceValue(id); + @LayoutlibDelegate + static String[] getStringArray(Resources resources, int id) throws NotFoundException { + ResourceValue resValue = getArrayResourceValue(resources, 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())}; + resolveReference(resources, resValue.getValue(), resValue.isFramework())}; } ArrayResourceValue arv = ((ArrayResourceValue) resValue); - return fillValues(arv, new String[arv.getElementCount()]); + return fillValues(resources, arv, new String[arv.getElementCount()]); } /** @@ -276,25 +277,26 @@ public final class BridgeResources extends Resources { * always Strings. The ideal signature for the method should be <T super String>, but java * generics don't support it. */ - T[] fillValues(ArrayResourceValue resValue, T[] values) { + static T[] fillValues(Resources resources, 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()); + T s = (T) resolveReference(resources, iterator.next(), resValue.isFramework()); values[i] = s; } return values; } - @Override - public int[] getIntArray(int id) throws NotFoundException { - ResourceValue rv = getArrayResourceValue(id); + @LayoutlibDelegate + static int[] getIntArray(Resources resources, int id) throws NotFoundException { + ResourceValue rv = getArrayResourceValue(resources, 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()); + String firstValue = resolveReference(resources, rv.getValue(), rv.isFramework()); try { return new int[]{getInt(firstValue)}; } catch (NumberFormatException e) { @@ -308,7 +310,7 @@ public final class BridgeResources extends Resources { int[] values = new int[resValue.getElementCount()]; int i = 0; for (Iterator iterator = resValue.iterator(); iterator.hasNext(); i++) { - String element = resolveReference(iterator.next(), resValue.isFramework()); + String element = resolveReference(resources, iterator.next(), resValue.isFramework()); try { values[i] = getInt(element); } catch (NumberFormatException e) { @@ -328,11 +330,13 @@ public final class BridgeResources extends Resources { * 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); + private static ResourceValue getArrayResourceValue(Resources resources, int id) + throws NotFoundException { + Pair v = getResourceValue(resources, id, mPlatformResourceFlag); if (v != null) { ResourceValue resValue = v.getSecond(); @@ -358,19 +362,20 @@ public final class BridgeResources extends Resources { } // id was not found or not resolved. Throw a NotFoundException. - throwException(id); + throwException(resources, id); // this is not used since the method above always throws return null; } @NonNull - private String resolveReference(@NonNull String ref, boolean forceFrameworkOnly) { + private static String resolveReference(Resources resources, @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); + resources.mContext.getRenderResources().findResValue(ref, forceFrameworkOnly); + rv = resources.mContext.getRenderResources().resolveResValue(rv); if (rv != null) { return rv.getValue(); } else { @@ -382,9 +387,9 @@ public final class BridgeResources extends Resources { return ref; } - @Override - public XmlResourceParser getLayout(int id) throws NotFoundException { - Pair v = getResourceValue(id, mPlatformResourceFlag); + @LayoutlibDelegate + static XmlResourceParser getLayout(Resources resources, int id) throws NotFoundException { + Pair v = getResourceValue(resources, id, mPlatformResourceFlag); if (v != null) { ResourceValue value = v.getSecond(); @@ -392,8 +397,8 @@ public final class BridgeResources extends Resources { try { // check if the current parser can provide us with a custom parser. - if (mPlatformResourceFlag[0] == false) { - parser = mLayoutlibCallback.getParser(value); + if (!mPlatformResourceFlag[0]) { + parser = resources.mLayoutlibCallback.getParser(value); } // create a new one manually if needed. @@ -407,7 +412,8 @@ public final class BridgeResources extends Resources { } if (parser != null) { - return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]); + return new BridgeXmlBlockParser(parser, resources.mContext, + mPlatformResourceFlag[0]); } } catch (XmlPullParserException e) { Bridge.getLog().error(LayoutLog.TAG_BROKEN, @@ -420,19 +426,19 @@ public final class BridgeResources extends Resources { } // id was not found or not resolved. Throw a NotFoundException. - throwException(id); + throwException(resources, id); // this is not used since the method above always throws return null; } - @Override - public XmlResourceParser getAnimation(int id) throws NotFoundException { - Pair v = getResourceValue(id, mPlatformResourceFlag); + @LayoutlibDelegate + static XmlResourceParser getAnimation(Resources resources, int id) throws NotFoundException { + Pair v = getResourceValue(resources, id, mPlatformResourceFlag); if (v != null) { ResourceValue value = v.getSecond(); - XmlPullParser parser = null; + XmlPullParser parser; try { File xml = new File(value.getValue()); @@ -441,7 +447,8 @@ public final class BridgeResources extends Resources { // give that to our XmlBlockParser parser = ParserFactory.create(xml); - return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]); + return new BridgeXmlBlockParser(parser, resources.mContext, + mPlatformResourceFlag[0]); } } catch (XmlPullParserException e) { Bridge.getLog().error(LayoutLog.TAG_BROKEN, @@ -454,26 +461,31 @@ public final class BridgeResources extends Resources { } // id was not found or not resolved. Throw a NotFoundException. - throwException(id); + throwException(resources, id); // this is not used since the method above always throws return null; } - @Override - public TypedArray obtainAttributes(AttributeSet set, int[] attrs) { - return mContext.obtainStyledAttributes(set, attrs); + @LayoutlibDelegate + static TypedArray obtainAttributes(Resources resources, AttributeSet set, int[] attrs) { + return resources.mContext.obtainStyledAttributes(set, attrs); } - @Override - public TypedArray obtainTypedArray(int id) throws NotFoundException { + @LayoutlibDelegate + static TypedArray obtainAttributes(Resources resources, Resources.Theme theme, AttributeSet + set, int[] attrs) { + return Resources.obtainAttributes_Original(resources, theme, set, attrs); + } + + @LayoutlibDelegate + static TypedArray obtainTypedArray(Resources resources, int id) throws NotFoundException { throw new UnsupportedOperationException(); } - - @Override - public float getDimension(int id) throws NotFoundException { - Pair value = getResourceValue(id, mPlatformResourceFlag); + @LayoutlibDelegate + static float getDimension(Resources resources, int id) throws NotFoundException { + Pair value = getResourceValue(resources, id, mPlatformResourceFlag); if (value != null) { ResourceValue resValue = value.getSecond(); @@ -488,26 +500,26 @@ public final class BridgeResources extends Resources { } else if (v.equals(BridgeConstants.WRAP_CONTENT)) { return LayoutParams.WRAP_CONTENT; } - + TypedValue tmpValue = new TypedValue(); if (ResourceHelper.parseFloatAttribute( - value.getFirst(), v, mTmpValue, true /*requireUnit*/) && - mTmpValue.type == TypedValue.TYPE_DIMENSION) { - return mTmpValue.getDimension(getDisplayMetrics()); + value.getFirst(), v, tmpValue, true /*requireUnit*/) && + tmpValue.type == TypedValue.TYPE_DIMENSION) { + return tmpValue.getDimension(resources.getDisplayMetrics()); } } } } // id was not found or not resolved. Throw a NotFoundException. - throwException(id); + throwException(resources, id); // this is not used since the method above always throws return 0; } - @Override - public int getDimensionPixelOffset(int id) throws NotFoundException { - Pair value = getResourceValue(id, mPlatformResourceFlag); + @LayoutlibDelegate + static int getDimensionPixelOffset(Resources resources, int id) throws NotFoundException { + Pair value = getResourceValue(resources, id, mPlatformResourceFlag); if (value != null) { ResourceValue resValue = value.getSecond(); @@ -516,26 +528,27 @@ public final class BridgeResources extends Resources { if (resValue != null) { String v = resValue.getValue(); if (v != null) { + TypedValue tmpValue = new TypedValue(); if (ResourceHelper.parseFloatAttribute( - value.getFirst(), v, mTmpValue, true /*requireUnit*/) && - mTmpValue.type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelOffset(mTmpValue.data, - getDisplayMetrics()); + value.getFirst(), v, tmpValue, true /*requireUnit*/) && + tmpValue.type == TypedValue.TYPE_DIMENSION) { + return TypedValue.complexToDimensionPixelOffset(tmpValue.data, + resources.getDisplayMetrics()); } } } } // id was not found or not resolved. Throw a NotFoundException. - throwException(id); + throwException(resources, id); // this is not used since the method above always throws return 0; } - @Override - public int getDimensionPixelSize(int id) throws NotFoundException { - Pair value = getResourceValue(id, mPlatformResourceFlag); + @LayoutlibDelegate + static int getDimensionPixelSize(Resources resources, int id) throws NotFoundException { + Pair value = getResourceValue(resources, id, mPlatformResourceFlag); if (value != null) { ResourceValue resValue = value.getSecond(); @@ -544,26 +557,27 @@ public final class BridgeResources extends Resources { if (resValue != null) { String v = resValue.getValue(); if (v != null) { + TypedValue tmpValue = new TypedValue(); if (ResourceHelper.parseFloatAttribute( - value.getFirst(), v, mTmpValue, true /*requireUnit*/) && - mTmpValue.type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize(mTmpValue.data, - getDisplayMetrics()); + value.getFirst(), v, tmpValue, true /*requireUnit*/) && + tmpValue.type == TypedValue.TYPE_DIMENSION) { + return TypedValue.complexToDimensionPixelSize(tmpValue.data, + resources.getDisplayMetrics()); } } } } // id was not found or not resolved. Throw a NotFoundException. - throwException(id); + throwException(resources, id); // this is not used since the method above always throws return 0; } - @Override - public int getInteger(int id) throws NotFoundException { - Pair value = getResourceValue(id, mPlatformResourceFlag); + @LayoutlibDelegate + static int getInteger(Resources resources, int id) throws NotFoundException { + Pair value = getResourceValue(resources, id, mPlatformResourceFlag); if (value != null) { ResourceValue resValue = value.getSecond(); @@ -582,15 +596,15 @@ public final class BridgeResources extends Resources { } // id was not found or not resolved. Throw a NotFoundException. - throwException(id); + throwException(resources, id); // this is not used since the method above always throws return 0; } - @Override - public boolean getBoolean(int id) throws NotFoundException { - Pair value = getResourceValue(id, mPlatformResourceFlag); + @LayoutlibDelegate + static boolean getBoolean(Resources resources, int id) throws NotFoundException { + Pair value = getResourceValue(resources, id, mPlatformResourceFlag); if (value != null) { ResourceValue resValue = value.getSecond(); @@ -605,61 +619,62 @@ public final class BridgeResources extends Resources { } // id was not found or not resolved. Throw a NotFoundException. - throwException(id); + throwException(resources, id); // this is not used since the method above always throws return false; } - @Override - public String getResourceEntryName(int resid) throws NotFoundException { + @LayoutlibDelegate + static String getResourceEntryName(Resources resources, int resid) throws NotFoundException { throw new UnsupportedOperationException(); } - @Override - public String getResourceName(int resid) throws NotFoundException { + @LayoutlibDelegate + static String getResourceName(Resources resources, int resid) throws NotFoundException { throw new UnsupportedOperationException(); } - @Override - public String getResourceTypeName(int resid) throws NotFoundException { + @LayoutlibDelegate + static String getResourceTypeName(Resources resources, int resid) throws NotFoundException { throw new UnsupportedOperationException(); } - @Override - public String getString(int id, Object... formatArgs) throws NotFoundException { - String s = getString(id); + @LayoutlibDelegate + static String getString(Resources resources, int id, Object... formatArgs) + throws NotFoundException { + String s = getString(resources, id); if (s != null) { return String.format(s, formatArgs); } // id was not found or not resolved. Throw a NotFoundException. - throwException(id); + throwException(resources, id); // this is not used since the method above always throws return null; } - @Override - public String getString(int id) throws NotFoundException { - Pair value = getResourceValue(id, mPlatformResourceFlag); + @LayoutlibDelegate + static String getString(Resources resources, int id) throws NotFoundException { + Pair value = getResourceValue(resources, id, mPlatformResourceFlag); if (value != null && value.getSecond().getValue() != null) { return value.getSecond().getValue(); } // id was not found or not resolved. Throw a NotFoundException. - throwException(id); + throwException(resources, id); // this is not used since the method above always throws return null; } - @Override - public void getValue(int id, TypedValue outValue, boolean resolveRefs) + @LayoutlibDelegate + static void getValue(Resources resources, int id, TypedValue outValue, boolean resolveRefs) throws NotFoundException { - Pair value = getResourceValue(id, mPlatformResourceFlag); + Pair value = getResourceValue(resources, id, mPlatformResourceFlag); if (value != null) { ResourceValue resVal = value.getSecond(); @@ -672,7 +687,7 @@ public final class BridgeResources extends Resources { } if (resVal instanceof DensityBasedResourceValue) { outValue.density = - ((DensityBasedResourceValue) resVal).getResourceDensity().getDpiValue(); + ((DensityBasedResourceValue) resVal).getResourceDensity().getDpiValue(); } // else it's a string @@ -683,18 +698,18 @@ public final class BridgeResources extends Resources { } // id was not found or not resolved. Throw a NotFoundException. - throwException(id); + throwException(resources, id); } - @Override - public void getValue(String name, TypedValue outValue, boolean resolveRefs) + @LayoutlibDelegate + static void getValue(Resources resources, String name, TypedValue outValue, boolean resolveRefs) throws NotFoundException { throw new UnsupportedOperationException(); } - @Override - public XmlResourceParser getXml(int id) throws NotFoundException { - Pair value = getResourceValue(id, mPlatformResourceFlag); + @LayoutlibDelegate + static XmlResourceParser getXml(Resources resources, int id) throws NotFoundException { + Pair value = getResourceValue(resources, id, mPlatformResourceFlag); if (value != null) { String v = value.getSecond().getValue(); @@ -706,7 +721,8 @@ public final class BridgeResources extends Resources { try { XmlPullParser parser = ParserFactory.create(f); - return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]); + return new BridgeXmlBlockParser(parser, resources.mContext, + mPlatformResourceFlag[0]); } catch (XmlPullParserException e) { NotFoundException newE = new NotFoundException(); newE.initCause(e); @@ -721,25 +737,31 @@ public final class BridgeResources extends Resources { } // id was not found or not resolved. Throw a NotFoundException. - throwException(id); + throwException(resources, id); // this is not used since the method above always throws return null; } - @Override - public XmlResourceParser loadXmlResourceParser(String file, int id, + @LayoutlibDelegate + static XmlResourceParser loadXmlResourceParser(Resources resources, int id, + String type) throws NotFoundException { + return resources.loadXmlResourceParser_Original(id, type); + } + + @LayoutlibDelegate + static XmlResourceParser loadXmlResourceParser(Resources resources, String file, int id, int assetCookie, String type) throws NotFoundException { // even though we know the XML file to load directly, we still need to resolve the // id so that we can know if it's a platform or project resource. // (mPlatformResouceFlag will get the result and will be used later). - getResourceValue(id, mPlatformResourceFlag); + getResourceValue(resources, id, mPlatformResourceFlag); File f = new File(file); try { XmlPullParser parser = ParserFactory.create(f); - return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]); + return new BridgeXmlBlockParser(parser, resources.mContext, mPlatformResourceFlag[0]); } catch (XmlPullParserException e) { NotFoundException newE = new NotFoundException(); newE.initCause(e); @@ -751,9 +773,9 @@ public final class BridgeResources extends Resources { } } - @Override - public InputStream openRawResource(int id) throws NotFoundException { - Pair value = getResourceValue(id, mPlatformResourceFlag); + @LayoutlibDelegate + static InputStream openRawResource(Resources resources, int id) throws NotFoundException { + Pair value = getResourceValue(resources, id, mPlatformResourceFlag); if (value != null) { String path = value.getSecond().getValue(); @@ -780,15 +802,16 @@ public final class BridgeResources extends Resources { } // id was not found or not resolved. Throw a NotFoundException. - throwException(id); + throwException(resources, id); // this is not used since the method above always throws return null; } - @Override - public InputStream openRawResource(int id, TypedValue value) throws NotFoundException { - getValue(id, value, true); + @LayoutlibDelegate + static InputStream openRawResource(Resources resources, int id, TypedValue value) throws + NotFoundException { + getValue(resources, id, value, true); String path = value.string.toString(); @@ -812,23 +835,27 @@ public final class BridgeResources extends Resources { throw new NotFoundException(); } - @Override - public AssetFileDescriptor openRawResourceFd(int id) throws NotFoundException { + @LayoutlibDelegate + static AssetFileDescriptor openRawResourceFd(Resources resources, int id) throws + NotFoundException { throw new UnsupportedOperationException(); } /** - * Builds and throws a {@link Resources.NotFoundException} based on a resource id and a resource type. + * Builds and throws a {@link Resources.NotFoundException} based on a resource id and a resource + * type. + * * @param id the id of the resource + * * @throws NotFoundException */ - private void throwException(int id) throws NotFoundException { + private static void throwException(Resources resources, int id) throws NotFoundException { // first get the String related to this id in the framework Pair resourceInfo = Bridge.resolveResourceId(id); // if the name is unknown in the framework, get it from the custom view loader. - if (resourceInfo == null && mLayoutlibCallback != null) { - resourceInfo = mLayoutlibCallback.resolveResourceId(id); + if (resourceInfo == null && resources.mLayoutlibCallback != null) { + resourceInfo = resources.mLayoutlibCallback.resolveResourceId(id); } String message; @@ -844,7 +871,7 @@ public final class BridgeResources extends Resources { throw new NotFoundException(message); } - private int getInt(String v) throws NumberFormatException { + private static int getInt(String v) throws NumberFormatException { int radix = 10; if (v.startsWith("0x")) { v = v.substring(2); diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java index 8d5863be918bb..8bd2a7acafdc7 100644 --- a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java @@ -23,7 +23,7 @@ import com.android.resources.Density; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.annotation.Nullable; -import android.content.res.BridgeResources.NinePatchInputStream; +import com.android.layoutlib.bridge.util.NinePatchInputStream; import android.graphics.BitmapFactory.Options; import android.graphics.Bitmap_Delegate.BitmapCreateFlags; 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 a32128f656c4b..3a491f2a8f301 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 @@ -52,11 +52,11 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.content.res.BridgeAssetManager; -import android.content.res.BridgeResources; import android.content.res.BridgeTypedArray; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.Resources.Theme; +import android.content.res.Resources_Delegate; import android.database.DatabaseErrorHandler; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; @@ -223,7 +223,7 @@ public final class BridgeContext extends Context { public void initResources() { AssetManager assetManager = AssetManager.getSystem(); - mSystemResources = BridgeResources.initSystem( + mSystemResources = Resources_Delegate.initSystem( this, assetManager, mMetrics, @@ -236,7 +236,7 @@ public final class BridgeContext extends Context { * Disposes the {@link Resources} singleton. */ public void disposeResources() { - BridgeResources.disposeSystem(); + Resources_Delegate.disposeSystem(); } public void setBridgeInflater(BridgeInflater inflater) { @@ -705,8 +705,8 @@ public final class BridgeContext extends Context { List> attributeList = searchAttrs(attrs); - BridgeTypedArray ta = ((BridgeResources) mSystemResources).newTypeArray(attrs.length, - isPlatformFile); + BridgeTypedArray ta = + Resources_Delegate.newTypeArray(mSystemResources, attrs.length, isPlatformFile); // look for a custom style. String customStyle = null; @@ -940,7 +940,7 @@ public final class BridgeContext extends Context { List> attributes = searchAttrs(attrs); - BridgeTypedArray ta = ((BridgeResources) mSystemResources).newTypeArray(attrs.length, + BridgeTypedArray ta = Resources_Delegate.newTypeArray(mSystemResources, attrs.length, false); // for each attribute, get its name so that we can search it in the style diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java new file mode 100644 index 0000000000000..96b795a05e089 --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2016 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 com.android.layoutlib.bridge.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; + +/** + * Simpler wrapper around FileInputStream. This is used when the input stream represent + * not a normal bitmap but a nine patch. + * This is useful when the InputStream is created in a method but used in another that needs + * to know whether this is 9-patch or not, such as BitmapFactory. + */ +public class NinePatchInputStream extends FileInputStream { + private boolean mFakeMarkSupport = true; + public NinePatchInputStream(File file) throws FileNotFoundException { + super(file); + } + + @Override + public boolean markSupported() { + if (mFakeMarkSupport) { + // this is needed so that BitmapFactory doesn't wrap this in a BufferedInputStream. + return true; + } + + return super.markSupported(); + } + + public void disableFakeMarkSupport() { + // disable fake mark support so that in case codec actually try to use them + // we don't lie to them. + mFakeMarkSupport = false; + } +} diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java index 8f0ad01c6dc3a..414b255be0b61 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java @@ -367,6 +367,10 @@ public class AsmGenerator { ClassVisitor cv = cw; + // FIXME Generify + if ("android/content/res/Resources".equals(className)) { + cv = new FieldInjectorAdapter(cv); + } if (mReplaceMethodCallsClasses.contains(className)) { cv = new ReplaceMethodCallsAdapter(cv, className); } @@ -445,4 +449,5 @@ public class AsmGenerator { } return buffer.toByteArray(); } + } diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java index 9912b75c97cba..e17f1f891dddb 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java @@ -158,6 +158,31 @@ public final class CreateInfo implements ICreateInfo { */ public final static String[] DELEGATE_METHODS = new String[] { "android.app.Fragment#instantiate", //(Landroid/content/Context;Ljava/lang/String;Landroid/os/Bundle;)Landroid/app/Fragment;", + "android.content.res.Resources#getAnimation", + "android.content.res.Resources#getBoolean", + "android.content.res.Resources#getColor", + "android.content.res.Resources#getColorStateList", + "android.content.res.Resources#getDimension", + "android.content.res.Resources#getDimensionPixelOffset", + "android.content.res.Resources#getDimensionPixelSize", + "android.content.res.Resources#getDrawable", + "android.content.res.Resources#getIntArray", + "android.content.res.Resources#getInteger", + "android.content.res.Resources#getLayout", + "android.content.res.Resources#getResourceEntryName", + "android.content.res.Resources#getResourceName", + "android.content.res.Resources#getResourceTypeName", + "android.content.res.Resources#getString", + "android.content.res.Resources#getStringArray", + "android.content.res.Resources#getText", + "android.content.res.Resources#getTextArray", + "android.content.res.Resources#getValue", + "android.content.res.Resources#getXml", + "android.content.res.Resources#loadXmlResourceParser", + "android.content.res.Resources#obtainAttributes", + "android.content.res.Resources#obtainTypedArray", + "android.content.res.Resources#openRawResource", + "android.content.res.Resources#openRawResourceFd", "android.content.res.Resources$Theme#obtainStyledAttributes", "android.content.res.Resources$Theme#resolveAttribute", "android.content.res.Resources$Theme#resolveAttributes", @@ -317,7 +342,7 @@ public final class CreateInfo implements ICreateInfo { // Use android.icu.text versions of DateFormat and SimpleDateFormat since the // original ones do not match the Android implementation "java.text.DateFormat", "android.icu.text.DateFormat", - "java.text.SimpleDateFormat", "android.icu.text.SimpleDateFormat" + "java.text.SimpleDateFormat", "android.icu.text.SimpleDateFormat", }; private final static String[] EXCLUDED_CLASSES = diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/FieldInjectorAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/FieldInjectorAdapter.java new file mode 100644 index 0000000000000..4608a84af33dd --- /dev/null +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/FieldInjectorAdapter.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2016 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 com.android.tools.layoutlib.create; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Opcodes; + +/** + * Injects fields in a class. + *

+ * TODO: Generify + */ +public class FieldInjectorAdapter extends ClassVisitor { + public FieldInjectorAdapter(ClassVisitor cv) { + super(Opcodes.ASM4, cv); + } + + @Override + public void visitEnd() { + super.visitField(Opcodes.ACC_PUBLIC, "mLayoutlibCallback", + "Lcom/android/ide/common/rendering/api/LayoutlibCallback;", null, null); + super.visitField(Opcodes.ACC_PUBLIC, "mContext", + "Lcom/android/layoutlib/bridge/android/BridgeContext;", null, null); + super.visitEnd(); + } +}