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
This commit is contained in:
Deepanshu Gupta
2016-03-04 19:31:28 +05:30
parent b75a0426d8
commit 9b137e27d3
8 changed files with 367 additions and 220 deletions

View File

@@ -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);
}
}

View File

@@ -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()}.
* <p/>
* They will end up using our bridge resources.
* <p/>
* {@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<String, ResourceValue> getResourceValue(int id, boolean[] platformResFlag_out) {
private static Pair<String, ResourceValue> getResourceValue(Resources resources, int id,
boolean[] platformResFlag_out) {
// first get the String related to this id in the framework
Pair<ResourceType, String> 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<String, ResourceValue> 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<String, ResourceValue> 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<String, ResourceValue> 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<String, ResourceValue> 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<String, ResourceValue> 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<String, ResourceValue> 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<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
@LayoutlibDelegate
static CharSequence getText(Resources resources, int id, CharSequence def) {
Pair<String, ResourceValue> 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<String, ResourceValue> 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 &lt;T super String&gt;, but java
* generics don't support it.
*/
<T extends CharSequence> T[] fillValues(ArrayResourceValue resValue, T[] values) {
static <T extends CharSequence> T[] fillValues(Resources resources, ArrayResourceValue resValue,
T[] values) {
int i = 0;
for (Iterator<String> 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<String> 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.
* <p/>
*
* @throws NotFoundException if no resource if found
*/
@Nullable
private ResourceValue getArrayResourceValue(int id) throws NotFoundException {
Pair<String, ResourceValue> v = getResourceValue(id, mPlatformResourceFlag);
private static ResourceValue getArrayResourceValue(Resources resources, int id)
throws NotFoundException {
Pair<String, ResourceValue> 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<String, ResourceValue> v = getResourceValue(id, mPlatformResourceFlag);
@LayoutlibDelegate
static XmlResourceParser getLayout(Resources resources, int id) throws NotFoundException {
Pair<String, ResourceValue> 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<String, ResourceValue> v = getResourceValue(id, mPlatformResourceFlag);
@LayoutlibDelegate
static XmlResourceParser getAnimation(Resources resources, int id) throws NotFoundException {
Pair<String, ResourceValue> 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<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
@LayoutlibDelegate
static float getDimension(Resources resources, int id) throws NotFoundException {
Pair<String, ResourceValue> 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<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
@LayoutlibDelegate
static int getDimensionPixelOffset(Resources resources, int id) throws NotFoundException {
Pair<String, ResourceValue> 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<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
@LayoutlibDelegate
static int getDimensionPixelSize(Resources resources, int id) throws NotFoundException {
Pair<String, ResourceValue> 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<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
@LayoutlibDelegate
static int getInteger(Resources resources, int id) throws NotFoundException {
Pair<String, ResourceValue> 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<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
@LayoutlibDelegate
static boolean getBoolean(Resources resources, int id) throws NotFoundException {
Pair<String, ResourceValue> 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<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
@LayoutlibDelegate
static String getString(Resources resources, int id) throws NotFoundException {
Pair<String, ResourceValue> 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<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
Pair<String, ResourceValue> 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<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
@LayoutlibDelegate
static XmlResourceParser getXml(Resources resources, int id) throws NotFoundException {
Pair<String, ResourceValue> 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<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
@LayoutlibDelegate
static InputStream openRawResource(Resources resources, int id) throws NotFoundException {
Pair<String, ResourceValue> 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<ResourceType, String> 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);

View File

@@ -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;

View File

@@ -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<Pair<String, Boolean>> 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<Pair<String, Boolean>> 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

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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 =

View File

@@ -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.
* <p>
* 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();
}
}