diff options
author | Sergey Prigogin <sprigogin@google.com> | 2018-05-02 16:46:32 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2018-05-02 16:46:32 +0000 |
commit | 9cbea8e8d1028641fe78e26f65b178954909bd71 (patch) | |
tree | 1cecc3c76df66b821caab5862f9284692bb0fc44 | |
parent | 54cff2c1941cf39d037c1d2f5e2a00503a95d5ca (diff) | |
parent | 0cf4b48ebe0b89025e49cccffcb8cc4c20efefc2 (diff) | |
download | layoutlib-9cbea8e8d1028641fe78e26f65b178954909bd71.tar.gz |
Merge "Delegate resource file operations to AssetRepository" into pi-layoutlib-dev
16 files changed, 308 insertions, 282 deletions
diff --git a/bridge/src/android/content/res/Resources_Delegate.java b/bridge/src/android/content/res/Resources_Delegate.java index 2cbf86c7c4..84749c4cf3 100644 --- a/bridge/src/android/content/res/Resources_Delegate.java +++ b/bridge/src/android/content/res/Resources_Delegate.java @@ -18,6 +18,7 @@ package android.content.res; import com.android.SdkConstants; import com.android.ide.common.rendering.api.ArrayResourceValue; +import com.android.ide.common.rendering.api.AssetRepository; import com.android.ide.common.rendering.api.DensityBasedResourceValue; import com.android.ide.common.rendering.api.LayoutLog; import com.android.ide.common.rendering.api.LayoutlibCallback; @@ -59,14 +60,13 @@ import android.util.TypedValue; import android.view.DisplayAdjustments; import android.view.ViewGroup.LayoutParams; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; +import java.io.IOException; import java.io.InputStream; import java.util.Iterator; import java.util.Objects; import java.util.WeakHashMap; +import static android.content.res.AssetManager.ACCESS_STREAMING; import static com.android.SdkConstants.ANDROID_PKG; import static com.android.SdkConstants.PREFIX_RESOURCE_REF; @@ -230,8 +230,9 @@ public class Resources_Delegate { } catch (NumberFormatException e) { // Check if the value passed is a file. If it is, mostly likely, user is referencing // a color state list from a place where they should reference only a pure color. + AssetRepository repository = getAssetRepository(resources); String message; - if (new File(resourceValue.getValue()).isFile()) { + if (repository.isFileResource(resourceValue.getValue())) { String resource = (resourceValue.isFramework() ? "@android:" : "@") + "color/" + resourceValue.getName(); message = "Hexadecimal color expected, found Color State List for " + resource; @@ -476,12 +477,9 @@ public class Resources_Delegate { return ResourceHelper.getXmlBlockParser(getContext(resources), value); } catch (XmlPullParserException e) { Bridge.getLog().error(LayoutLog.TAG_BROKEN, - "Failed to configure parser for " + value.getValue(), e, null /*data*/); + "Failed to parse " + value.getValue(), e, null /*data*/); // we'll return null below. - } catch (FileNotFoundException e) { - // this shouldn't happen since we check above. } - } // id was not found or not resolved. Throw a NotFoundException. @@ -502,12 +500,9 @@ public class Resources_Delegate { return ResourceHelper.getXmlBlockParser(getContext(resources), value); } catch (XmlPullParserException e) { Bridge.getLog().error(LayoutLog.TAG_BROKEN, - "Failed to configure parser for " + value.getValue(), e, null /*data*/); + "Failed to parse " + value.getValue(), e, null /*data*/); // we'll return null below. - } catch (FileNotFoundException e) { - // this shouldn't happen since we check above. } - } // id was not found or not resolved. Throw a NotFoundException. @@ -904,10 +899,8 @@ public class Resources_Delegate { return ResourceHelper.getXmlBlockParser(getContext(resources), value); } catch (XmlPullParserException e) { Bridge.getLog().error(LayoutLog.TAG_BROKEN, - "Failed to configure parser for " + value.getValue(), e, null /*data*/); + "Failed to parse " + value.getValue(), e, null /*data*/); // we'll return null below. - } catch (FileNotFoundException e) { - // this shouldn't happen since we check above. } } @@ -930,8 +923,7 @@ public class Resources_Delegate { // 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). - Pair<String, ResourceValue> result = - getResourceValue(resources, id, mPlatformResourceFlag); + Pair<String, ResourceValue> result = getResourceValue(resources, id, mPlatformResourceFlag); ResourceNamespace layoutNamespace; if (result != null && result.getSecond() != null) { @@ -942,19 +934,13 @@ public class Resources_Delegate { layoutNamespace = ResourceNamespace.RES_AUTO; } - File f = new File(file); try { - XmlPullParser parser = ParserFactory.create(f); - + XmlPullParser parser = ParserFactory.create(file); return new BridgeXmlBlockParser(parser, getContext(resources), layoutNamespace); } catch (XmlPullParserException e) { NotFoundException newE = new NotFoundException(); newE.initCause(e); throw newE; - } catch (FileNotFoundException e) { - NotFoundException newE = new NotFoundException(); - newE.initCause(e); - throw newE; } } @@ -964,25 +950,8 @@ public class Resources_Delegate { if (value != null) { String path = value.getSecond().getValue(); - if (path != null) { - // check this is a file - File f = new File(path); - if (f.isFile()) { - try { - // if it's a nine-patch return a custom input stream so that - // other methods (mainly bitmap factory) can detect it's a 9-patch - // and actually load it as a 9-patch instead of a normal bitmap - if (path.toLowerCase().endsWith(NinePatch.EXTENSION_9PATCH)) { - return new NinePatchInputStream(f); - } - return new FileInputStream(f); - } catch (FileNotFoundException e) { - NotFoundException newE = new NotFoundException(); - newE.initCause(e); - throw newE; - } - } + return openRawResource(resources, path); } } @@ -994,35 +963,36 @@ public class Resources_Delegate { } @LayoutlibDelegate - static InputStream openRawResource(Resources resources, int id, TypedValue value) throws - NotFoundException { + static InputStream openRawResource(Resources resources, int id, TypedValue value) + throws NotFoundException { getValue(resources, id, value, true); String path = value.string.toString(); + return openRawResource(resources, path); + } - File f = new File(path); - if (f.isFile()) { - try { - // if it's a nine-patch return a custom input stream so that - // other methods (mainly bitmap factory) can detect it's a 9-patch - // and actually load it as a 9-patch instead of a normal bitmap - if (path.toLowerCase().endsWith(NinePatch.EXTENSION_9PATCH)) { - return new NinePatchInputStream(f); - } - return new FileInputStream(f); - } catch (FileNotFoundException e) { - NotFoundException exception = new NotFoundException(); - exception.initCause(e); - throw exception; + private static InputStream openRawResource(Resources resources, String path) + throws NotFoundException { + AssetRepository repository = getAssetRepository(resources); + try { + InputStream stream = repository.openNonAsset(0, path, ACCESS_STREAMING); + // If it's a nine-patch return a custom input stream so that + // other methods (mainly bitmap factory) can detect it's a 9-patch + // and actually load it as a 9-patch instead of a normal bitmap. + if (path.toLowerCase().endsWith(NinePatch.EXTENSION_9PATCH)) { + return new NinePatchInputStream(stream); } + return stream; + } catch (IOException e) { + NotFoundException exception = new NotFoundException(); + exception.initCause(e); + throw exception; } - - throw new NotFoundException(); } @LayoutlibDelegate - static AssetFileDescriptor openRawResourceFd(Resources resources, int id) throws - NotFoundException { + static AssetFileDescriptor openRawResourceFd(Resources resources, int id) + throws NotFoundException { throw new UnsupportedOperationException(); } @@ -1129,4 +1099,10 @@ public class Resources_Delegate { } return Integer.parseInt(v, radix); } + + private static AssetRepository getAssetRepository(Resources resources) { + BridgeContext context = getContext(resources); + BridgeAssetManager assetManager = context.getAssets(); + return assetManager.getAssetRepository(); + } } diff --git a/bridge/src/android/graphics/Bitmap_Delegate.java b/bridge/src/android/graphics/Bitmap_Delegate.java index 72344b3b0d..49cad43f30 100644 --- a/bridge/src/android/graphics/Bitmap_Delegate.java +++ b/bridge/src/android/graphics/Bitmap_Delegate.java @@ -60,7 +60,6 @@ import libcore.util.NativeAllocationRegistry_Delegate; */ public final class Bitmap_Delegate { - public enum BitmapCreateFlags { NONE, PREMULTIPLIED, MUTABLE } @@ -96,17 +95,17 @@ public final class Bitmap_Delegate { } /** - * Creates and returns a {@link Bitmap} initialized with the given file content. + * Creates and returns a {@link Bitmap} initialized with the given stream content. * - * @param input the file from which to read the bitmap content + * @param input the stream from which to read the bitmap content * @param isMutable whether the bitmap is mutable * @param density the density associated with the bitmap * * @see Bitmap#isMutable() * @see Bitmap#getDensity() */ - public static Bitmap createBitmap(File input, boolean isMutable, Density density) - throws IOException { + public static Bitmap createBitmap(@Nullable InputStream input, boolean isMutable, + Density density) throws IOException { return createBitmap(input, getPremultipliedBitmapCreateFlags(isMutable), density); } @@ -120,11 +119,11 @@ public final class Bitmap_Delegate { * @see Bitmap#isMutable() * @see Bitmap#getDensity() */ - private static Bitmap createBitmap(File input, Set<BitmapCreateFlags> createFlags, + static Bitmap createBitmap(@Nullable InputStream input, Set<BitmapCreateFlags> createFlags, Density density) throws IOException { // create a delegate with the content of the file. - BufferedImage image = ImageIO.read(input); - if (image == null && input.exists()) { + BufferedImage image = input == null ? null : ImageIO.read(input); + if (image == null) { // There was a problem decoding the image, or the decoder isn't registered. Webp maybe. // Replace with a broken image icon. BridgeContext currentContext = RenderAction.getCurrentContext(); @@ -144,39 +143,6 @@ public final class Bitmap_Delegate { } /** - * Creates and returns a {@link Bitmap} initialized with the given stream content. - * - * @param input the stream from which to read the bitmap content - * @param isMutable whether the bitmap is mutable - * @param density the density associated with the bitmap - * - * @see Bitmap#isMutable() - * @see Bitmap#getDensity() - */ - public static Bitmap createBitmap(InputStream input, boolean isMutable, Density density) - throws IOException { - return createBitmap(input, getPremultipliedBitmapCreateFlags(isMutable), density); - } - - /** - * Creates and returns a {@link Bitmap} initialized with the given stream content. - * - * @param input the stream from which to read the bitmap content - * @param density the density associated with the bitmap - * - * @see Bitmap#isPremultiplied() - * @see Bitmap#isMutable() - * @see Bitmap#getDensity() - */ - public static Bitmap createBitmap(InputStream input, Set<BitmapCreateFlags> createFlags, - Density density) throws IOException { - // create a delegate with the content of the stream. - Bitmap_Delegate delegate = new Bitmap_Delegate(ImageIO.read(input), Config.ARGB_8888); - - return createBitmap(delegate, createFlags, density.getDpiValue()); - } - - /** * Creates and returns a {@link Bitmap} initialized with the given {@link BufferedImage} * * @param image the bitmap content diff --git a/bridge/src/android/graphics/Typeface_Delegate.java b/bridge/src/android/graphics/Typeface_Delegate.java index c492cb9700..bde71415f8 100644 --- a/bridge/src/android/graphics/Typeface_Delegate.java +++ b/bridge/src/android/graphics/Typeface_Delegate.java @@ -24,7 +24,6 @@ import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; import com.android.layoutlib.bridge.android.RenderParamsFlags; import com.android.layoutlib.bridge.impl.DelegateManager; -import com.android.layoutlib.bridge.impl.ParserFactory; import com.android.layoutlib.bridge.impl.RenderAction; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; @@ -40,8 +39,6 @@ import android.text.FontConfig; import android.util.ArrayMap; import java.awt.Font; -import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; import java.lang.ref.SoftReference; import java.nio.ByteBuffer; @@ -272,20 +269,9 @@ public final class Typeface_Delegate { RenderParamsFlags.FLAG_KEY_XML_FILE_PARSER_SUPPORT); XmlPullParser parser = null; if (psiParserSupport != null && psiParserSupport) { - parser = context.getLayoutlibCallback().getXmlFileParser(path); + parser = context.getLayoutlibCallback().createXmlParserForPsiFile(path); } else { - File f = new File(path); - if (f.isFile()) { - try { - parser = ParserFactory.create(f); - } catch (XmlPullParserException | FileNotFoundException e) { - // this is an error and not warning since the file existence is checked - // before - // attempting to parse it. - Bridge.getLog().error(null, "Failed to parse file " + path, e, - null /*data*/); - } - } + parser = context.getLayoutlibCallback().createXmlParserForFile(path); } if (parser != null) { diff --git a/bridge/src/android/util/Xml_Delegate.java b/bridge/src/android/util/Xml_Delegate.java index 213e848659..e309dc6cfd 100644 --- a/bridge/src/android/util/Xml_Delegate.java +++ b/bridge/src/android/util/Xml_Delegate.java @@ -33,11 +33,10 @@ import org.xmlpull.v1.XmlPullParserException; * around to map int to instance of the delegate. */ public class Xml_Delegate { - @LayoutlibDelegate /*package*/ static XmlPullParser newPullParser() { try { - return ParserFactory.instantiateParser(null); + return ParserFactory.create(); } catch (XmlPullParserException e) { throw new AssertionError(); } diff --git a/bridge/src/android/view/BridgeInflater.java b/bridge/src/android/view/BridgeInflater.java index 603738f8ec..a13ee6cda5 100644 --- a/bridge/src/android/view/BridgeInflater.java +++ b/bridge/src/android/view/BridgeInflater.java @@ -347,21 +347,22 @@ public final class BridgeInflater extends LayoutInflater { } if (value != null) { - File f = new File(value.getValue()); - if (f.isFile()) { - try { - XmlPullParser parser = ParserFactory.create(f, true); + String path = value.getValue(); + try { + XmlPullParser parser = ParserFactory.create(path, true); + if (parser == null) { + return null; + } - BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser( - parser, bridgeContext, value.getNamespace()); + BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser( + parser, bridgeContext, value.getNamespace()); - return inflate(bridgeParser, root); - } catch (Exception e) { - Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ, - "Failed to parse file " + f.getAbsolutePath(), e, null); + return inflate(bridgeParser, root); + } catch (Exception e) { + Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ, + "Failed to parse file " + path, e, null); - return null; - } + return null; } } } diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index 086c33fbe4..1ed13dfb97 100644 --- a/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +++ b/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java @@ -465,39 +465,34 @@ public class BridgeContext extends Context { } if (resValue != null) { - - File xml = new File(resValue.getValue()); - if (xml.isFile()) { - // we need to create a pull parser around the layout XML file, and then - // give that to our XmlBlockParser - try { - XmlPullParser parser = ParserFactory.create(xml, true); - - // set the layout ref to have correct view cookies + String path = resValue.getValue(); + // We need to create a pull parser around the layout XML file, and then + // give that to our XmlBlockParser. + try { + XmlPullParser parser = ParserFactory.create(path, true); + if (parser != null) { + // Set the layout ref to have correct view cookies. mBridgeInflater.setResourceReference(layout); BridgeXmlBlockParser blockParser = - new BridgeXmlBlockParser(parser,this, layout.getNamespace()); + new BridgeXmlBlockParser(parser, this, layout.getNamespace()); try { pushParser(blockParser); - return Pair.of( - mBridgeInflater.inflate(blockParser, parent, attachToRoot), + return Pair.of(mBridgeInflater.inflate(blockParser, parent, attachToRoot), Boolean.FALSE); } finally { popParser(); } - } catch (XmlPullParserException e) { + } else { Bridge.getLog().error(LayoutLog.TAG_BROKEN, - "Failed to configure parser for " + xml, e, null /*data*/); - // we'll return null below. - } catch (FileNotFoundException e) { - // this shouldn't happen since we check above. - } finally { - mBridgeInflater.setResourceReference(null); + String.format("File %s is missing!", path), null); } - } else { + } catch (XmlPullParserException e) { Bridge.getLog().error(LayoutLog.TAG_BROKEN, - String.format("File %s is missing!", xml), null); + "Failed to parse file " + path, e, null /*data*/); + // We'll return null below. + } finally { + mBridgeInflater.setResourceReference(null); } } else { Bridge.getLog().error(LayoutLog.TAG_BROKEN, diff --git a/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java b/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java index c023e365e5..aa52ff538c 100644 --- a/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java +++ b/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java @@ -104,7 +104,7 @@ public class StatusBar extends CustomBar { try { BridgeXmlBlockParser parser = new BridgeXmlBlockParser( - ParserFactory.create(stream, null), + ParserFactory.create(stream, iconName), (BridgeContext) mContext, ResourceNamespace.ANDROID); imageView.setImageDrawable( diff --git a/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java b/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java index 1ae9cb646c..38b7aa5008 100644 --- a/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java +++ b/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java @@ -16,6 +16,7 @@ package com.android.layoutlib.bridge.impl; +import com.android.ide.common.rendering.api.XmlParserFactory; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -25,55 +26,34 @@ import android.annotation.Nullable; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; /** * A factory for {@link XmlPullParser}. - * */ public class ParserFactory { - public final static boolean LOG_PARSER = false; // Used to get a new XmlPullParser from the client. @Nullable - private static com.android.ide.common.rendering.api.ParserFactory sParserFactory; + private static XmlParserFactory sParserFactory; - public static void setParserFactory( - @Nullable com.android.ide.common.rendering.api.ParserFactory parserFactory) { + public static void setParserFactory(@Nullable XmlParserFactory parserFactory) { sParserFactory = parserFactory; } - @NonNull - public static XmlPullParser create(@NonNull File f) - throws XmlPullParserException, FileNotFoundException { - return create(f, false); - } - - public static XmlPullParser create(@NonNull File f, boolean isLayout) - throws XmlPullParserException, FileNotFoundException { - InputStream stream = new FileInputStream(f); - return create(stream, f.getName(), f.length(), isLayout); - } - @NonNull - public static XmlPullParser create(@NonNull InputStream stream, @Nullable String name) - throws XmlPullParserException { - return create(stream, name, -1, false); + @Nullable + public static XmlPullParser create(@NonNull String filePath) + throws XmlPullParserException { + return create(filePath, false); } - @NonNull - private static XmlPullParser create(@NonNull InputStream stream, @Nullable String name, - long size, boolean isLayout) throws XmlPullParserException { - XmlPullParser parser = instantiateParser(name); - - stream = readAndClose(stream, name, size); - - parser.setInput(stream, null); - if (isLayout) { + @Nullable + public static XmlPullParser create(@NonNull String filePath, boolean isLayout) + throws XmlPullParserException { + XmlPullParser parser = sParserFactory.createXmlParserForFile(filePath); + if (parser != null && isLayout) { try { return new LayoutParserWrapper(parser).peekTillLayoutStart(); } catch (IOException e) { @@ -84,46 +64,38 @@ public class ParserFactory { } @NonNull - public static XmlPullParser instantiateParser(@Nullable String name) + public static XmlPullParser create(@NonNull InputStream stream, @Nullable String name) throws XmlPullParserException { + XmlPullParser parser = create(); + + stream = readAndClose(stream, name); + + parser.setInput(stream, null); + return parser; + } + + @NonNull + public static XmlPullParser create() throws XmlPullParserException { if (sParserFactory == null) { throw new XmlPullParserException("ParserFactory not initialized."); } - XmlPullParser parser = sParserFactory.createParser(name); + XmlPullParser parser = sParserFactory.createXmlParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); return parser; } @NonNull - private static InputStream readAndClose(@NonNull InputStream stream, @Nullable String name, - long size) throws XmlPullParserException { - // just a sanity check. It's doubtful we'll have such big files! - if (size > Integer.MAX_VALUE) { - throw new XmlPullParserException("File " + name + " is too big to be parsed"); - } - int intSize = (int) size; - - // create a buffered reader to facilitate reading. - BufferedInputStream bufferedStream = new BufferedInputStream(stream); - try { - int avail; - if (intSize != -1) { - avail = intSize; - } else { - // get the size to read. - avail = bufferedStream.available(); - } + private static InputStream readAndClose(@NonNull InputStream stream, @Nullable String name) + throws XmlPullParserException { + // Create a buffered stream to facilitate reading. + try (BufferedInputStream bufferedStream = new BufferedInputStream(stream)) { + int avail = bufferedStream.available(); - // create the initial buffer and read it. + // Create the initial buffer and read it. byte[] buffer = new byte[avail]; int read = stream.read(buffer); - // this is the easy case. - if (read == intSize) { - return new ByteArrayInputStream(buffer); - } - - // check if there is more to read (read() does not necessarily read all that + // Check if there is more to read (read() does not necessarily read all that // available() returned!) while ((avail = bufferedStream.available()) > 0) { if (read + avail > buffer.length) { @@ -137,16 +109,10 @@ public class ParserFactory { read += stream.read(buffer, read, avail); } - // return a new stream encapsulating this buffer. + // Return a new stream encapsulating this buffer. return new ByteArrayInputStream(buffer); - } catch (IOException e) { throw new XmlPullParserException("Failed to read " + name, null, e); - } finally { - try { - bufferedStream.close(); - } catch (IOException ignored) { - } } } } diff --git a/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java index ae343ec892..91ed163673 100644 --- a/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java +++ b/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java @@ -233,7 +233,7 @@ public abstract class RenderAction<T extends RenderParams> { */ private void setUp() { // setup the ParserFactory - ParserFactory.setParserFactory(mParams.getLayoutlibCallback().getParserFactory()); + ParserFactory.setParserFactory(mParams.getLayoutlibCallback()); // make sure the Resources object references the context (and other objects) for this // scene diff --git a/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java index dd461dc4d3..04dc989af7 100644 --- a/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java +++ b/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java @@ -17,8 +17,10 @@ package com.android.layoutlib.bridge.impl; import com.android.SdkConstants; +import com.android.ide.common.rendering.api.AssetRepository; import com.android.ide.common.rendering.api.DensityBasedResourceValue; import com.android.ide.common.rendering.api.LayoutLog; +import com.android.ide.common.rendering.api.LayoutlibCallback; import com.android.ide.common.rendering.api.RenderResources; import com.android.ide.common.rendering.api.ResourceValue; import com.android.internal.util.XmlUtils; @@ -34,6 +36,7 @@ import org.xmlpull.v1.XmlPullParserException; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.res.BridgeAssetManager; import android.content.res.ColorStateList; import android.content.res.ComplexColor; import android.content.res.ComplexColor_Accessor; @@ -53,8 +56,6 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.NinePatchDrawable; import android.util.TypedValue; -import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; @@ -62,6 +63,8 @@ import java.net.MalformedURLException; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static android.content.res.AssetManager.ACCESS_STREAMING; + /** * Helper class to provide various conversion method used in handling android resources. */ @@ -252,8 +255,7 @@ public final class ResourceHelper { */ @Nullable public static BridgeXmlBlockParser getXmlBlockParser(@NonNull BridgeContext context, - @NonNull ResourceValue value) - throws FileNotFoundException, XmlPullParserException { + @NonNull ResourceValue value) throws XmlPullParserException { String stringValue = value.getValue(); if (RenderResources.REFERENCE_NULL.equals(stringValue)) { return null; @@ -261,17 +263,15 @@ public final class ResourceHelper { XmlPullParser parser = null; + LayoutlibCallback layoutlibCallback = context.getLayoutlibCallback(); // Framework values never need a PSI parser. They do not change and the do not contain // aapt:attr attributes. if (!value.isFramework()) { - parser = context.getLayoutlibCallback().getParser(value); + parser = layoutlibCallback.getParser(value); } if (parser == null) { - File xmlFile = new File(stringValue); - if (xmlFile.isFile()) { - parser = ParserFactory.create(xmlFile); - } + parser = ParserFactory.create(stringValue); } return parser == null @@ -312,16 +312,12 @@ public final class ResourceHelper { } if (lowerCaseValue.endsWith(NinePatch.EXTENSION_9PATCH)) { - File file = new File(stringValue); - if (file.isFile()) { - try { - return getNinePatchDrawable(new FileInputStream(file), density, - value.isFramework(), stringValue, context); - } catch (IOException e) { - // failed to read the file, we'll return null below. - Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ, - "Failed lot load " + file.getAbsolutePath(), e, null /*data*/); - } + try { + return getNinePatchDrawable(density, value.isFramework(), stringValue, context); + } catch (IOException e) { + // failed to read the file, we'll return null below. + Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ, + "Failed to load " + stringValue, e, null /*data*/); } return null; @@ -345,15 +341,22 @@ public final class ResourceHelper { return null; } else { - File bmpFile = new File(stringValue); - if (bmpFile.isFile()) { + AssetRepository repository = getAssetRepository(context); + if (repository.isFileResource(stringValue)) { try { Bitmap bitmap = Bridge.getCachedBitmap(stringValue, value.isFramework() ? null : context.getProjectKey()); if (bitmap == null) { + InputStream stream; + try { + stream = repository.openNonAsset(0, stringValue, ACCESS_STREAMING); + + } catch (FileNotFoundException e) { + stream = null; + } bitmap = - Bitmap_Delegate.createBitmap(bmpFile, false /*isMutable*/, density); + Bitmap_Delegate.createBitmap(stream, false /*isMutable*/, density); Bridge.setCachedBitmap(stringValue, bitmap, value.isFramework() ? null : context.getProjectKey()); } @@ -362,7 +365,7 @@ public final class ResourceHelper { } catch (IOException e) { // we'll return null below Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ, - "Failed lot load " + bmpFile.getAbsolutePath(), e, null /*data*/); + "Failed to load " + stringValue, e, null /*data*/); } } } @@ -370,6 +373,11 @@ public final class ResourceHelper { return null; } + private static AssetRepository getAssetRepository(@NonNull BridgeContext context) { + BridgeAssetManager assetManager = context.getAssets(); + return assetManager.getAssetRepository(); + } + /** * Returns a {@link Typeface} given a font name. The font name, can be a system font family * (like sans-serif) or a full path if the font is to be loaded from resources. @@ -402,24 +410,29 @@ public final class ResourceHelper { return getFont(value.getValue(), context, theme, value.isFramework()); } - private static Drawable getNinePatchDrawable(InputStream inputStream, Density density, - boolean isFramework, String cacheKey, BridgeContext context) throws IOException { + private static Drawable getNinePatchDrawable(Density density, boolean isFramework, + String path, BridgeContext context) throws IOException { // see if we still have both the chunk and the bitmap in the caches - NinePatchChunk chunk = Bridge.getCached9Patch(cacheKey, + NinePatchChunk chunk = Bridge.getCached9Patch(path, isFramework ? null : context.getProjectKey()); - Bitmap bitmap = Bridge.getCachedBitmap(cacheKey, + Bitmap bitmap = Bridge.getCachedBitmap(path, isFramework ? null : context.getProjectKey()); // if either chunk or bitmap is null, then we reload the 9-patch file. if (chunk == null || bitmap == null) { try { - NinePatch ninePatch = NinePatch.load(inputStream, true /*is9Patch*/, + AssetRepository repository = getAssetRepository(context); + if (!repository.isFileResource(path)) { + return null; + } + InputStream stream = repository.openNonAsset(0, path, ACCESS_STREAMING); + NinePatch ninePatch = NinePatch.load(stream, true /*is9Patch*/, false /* convert */); if (ninePatch != null) { if (chunk == null) { chunk = ninePatch.getChunk(); - Bridge.setCached9Patch(cacheKey, chunk, + Bridge.setCached9Patch(path, chunk, isFramework ? null : context.getProjectKey()); } @@ -428,7 +441,7 @@ public final class ResourceHelper { false /*isMutable*/, density); - Bridge.setCachedBitmap(cacheKey, bitmap, + Bridge.setCachedBitmap(path, bitmap, isFramework ? null : context.getProjectKey()); } } diff --git a/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java b/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java index f149b6cd8e..75e4a2b2bf 100644 --- a/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java +++ b/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java @@ -16,9 +16,13 @@ package com.android.layoutlib.bridge.util; +import com.android.tools.layoutlib.annotations.NotNull; + import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; /** * Simpler wrapper around FileInputStream. This is used when the input stream represent @@ -26,17 +30,22 @@ import java.io.FileNotFoundException; * 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 { +public class NinePatchInputStream extends InputStream { + private final InputStream mDelegate; private boolean mFakeMarkSupport = true; + public NinePatchInputStream(File file) throws FileNotFoundException { - super(file); + mDelegate = new FileInputStream(file); + } + + public NinePatchInputStream(@NotNull InputStream stream) { + mDelegate = stream; } @Override public boolean markSupported() { // this is needed so that BitmapFactory doesn't wrap this in a BufferedInputStream. - return mFakeMarkSupport || super.markSupported(); - + return mFakeMarkSupport || mDelegate.markSupported(); } public void disableFakeMarkSupport() { @@ -44,4 +53,44 @@ public class NinePatchInputStream extends FileInputStream { // we don't lie to them. mFakeMarkSupport = false; } + + @Override + public int read() throws IOException { + return mDelegate.read(); + } + + @Override + public int read(byte[] b) throws IOException { + return mDelegate.read(b); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + return mDelegate.read(b, off, len); + } + + @Override + public long skip(long n) throws IOException { + return mDelegate.skip(n); + } + + @Override + public int available() throws IOException { + return mDelegate.available(); + } + + @Override + public void close() throws IOException { + mDelegate.close(); + } + + @Override + public void mark(int readlimit) { + mDelegate.mark(readlimit); + } + + @Override + public void reset() throws IOException { + mDelegate.reset(); + } } diff --git a/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java b/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java index 24395f27eb..67dd7c5bf1 100644 --- a/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java +++ b/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java @@ -17,6 +17,7 @@ package com.android.layoutlib.bridge.android; import com.android.ide.common.rendering.api.ResourceNamespace; +import com.android.ide.common.rendering.api.XmlParserFactory; import com.android.layoutlib.bridge.impl.ParserFactory; import org.junit.AfterClass; @@ -28,6 +29,12 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import android.annotation.NonNull; +import android.annotation.Nullable; + +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; import static org.junit.Assert.assertEquals; @@ -40,7 +47,6 @@ public class BridgeXmlBlockParserTest { @Test public void testXmlBlockParser() throws Exception { - XmlPullParser parser = ParserFactory.create( getClass().getResourceAsStream("/com/android/layoutlib/testdata/layout1.xml"), "layout1.xml"); @@ -126,13 +132,30 @@ public class BridgeXmlBlockParserTest { ParserFactory.setParserFactory(null); } - private static class ParserFactoryImpl - extends com.android.ide.common.rendering.api.ParserFactory { + private static class ParserFactoryImpl implements XmlParserFactory { + @Override + @Nullable + public XmlPullParser createXmlParserForPsiFile(@NonNull String fileName) { + return createXmlParserForFile(fileName); + } - @NonNull @Override - public XmlPullParser createParser(String displayName) throws XmlPullParserException { - return new KXmlParser(); + @Nullable + public XmlPullParser createXmlParserForFile(@NonNull String fileName) { + try { + InputStream stream = new BufferedInputStream(new FileInputStream(fileName)); + XmlPullParser parser = new KXmlParser(); + parser.setInput(stream, null); + return parser; + } catch (FileNotFoundException | XmlPullParserException e) { + return null; + } + } + + @Override + @NonNull + public XmlPullParser createXmlParser() { + throw new UnsupportedOperationException(); } } } diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java index dac9570e9f..766cb11d28 100644 --- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java +++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java @@ -23,6 +23,7 @@ import com.android.ide.common.rendering.api.ResourceValue; import com.android.ide.common.rendering.api.SessionParams; import com.android.ide.common.rendering.api.SessionParams.RenderingMode; import com.android.ide.common.rendering.api.ViewInfo; +import com.android.ide.common.rendering.api.XmlParserFactory; import com.android.internal.R; import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.android.RenderParamsFlags; @@ -41,9 +42,9 @@ import org.junit.After; import org.junit.Test; import org.kxml2.io.KXmlParser; import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.res.AssetManager; import android.content.res.ColorStateList; import android.content.res.Configuration; @@ -776,9 +777,22 @@ public class RenderTests extends RenderTestBase { // Setup // Create the layout pull parser for our resources (empty.xml can not be part of the test // app as it won't compile). - ParserFactory.setParserFactory(new com.android.ide.common.rendering.api.ParserFactory() { + ParserFactory.setParserFactory(new XmlParserFactory() { @Override - public XmlPullParser createParser(String debugName) throws XmlPullParserException { + @Nullable + public XmlPullParser createXmlParserForPsiFile(@NonNull String fileName) { + return null; + } + + @Override + @Nullable + public XmlPullParser createXmlParserForFile(@NonNull String fileName) { + return null; + } + + @Override + @NonNull + public XmlPullParser createXmlParser() { return new KXmlParser(); } }); diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java index 37fc418d19..8056fa395b 100644 --- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java +++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java @@ -21,7 +21,6 @@ import com.android.ide.common.rendering.api.ActionBarCallback; import com.android.ide.common.rendering.api.AdapterBinding; import com.android.ide.common.rendering.api.ILayoutPullParser; import com.android.ide.common.rendering.api.LayoutlibCallback; -import com.android.ide.common.rendering.api.ParserFactory; import com.android.ide.common.rendering.api.ResourceReference; import com.android.ide.common.rendering.api.ResourceValue; import com.android.ide.common.rendering.api.SessionParams.Key; @@ -31,27 +30,32 @@ import com.android.utils.ILogger; import org.kxml2.io.KXmlParser; import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; import android.annotation.NonNull; import android.annotation.Nullable; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileInputStream; import java.io.FileNotFoundException; +import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.util.HashMap; import java.util.Map; -import com.google.android.collect.Maps; +import com.google.common.io.ByteStreams; import static com.android.ide.common.rendering.api.ResourceNamespace.RES_AUTO; public class LayoutLibTestCallback extends LayoutlibCallback { - private static final String PACKAGE_NAME = "com.android.layoutlib.test.myapplication"; - private final Map<Integer, ResourceReference> mProjectResources = Maps.newHashMap(); - private final Map<ResourceReference, Integer> mResources = Maps.newHashMap(); + private final Map<Integer, ResourceReference> mProjectResources = new HashMap<>(); + private final Map<ResourceReference, Integer> mResources = new HashMap<>(); private final ILogger mLog; private final ActionBarCallback mActionBarCallback = new ActionBarCallback(); private final ClassLoader mModuleClassLoader; @@ -153,16 +157,31 @@ public class LayoutLibTestCallback extends LayoutlibCallback { return false; } - @NonNull @Override - public ParserFactory getParserFactory() { - return new ParserFactory() { - @NonNull - @Override - public XmlPullParser createParser(@Nullable String debugName) { - return new KXmlParser(); - } - }; + @Nullable + public XmlPullParser createXmlParserForPsiFile(@NonNull String fileName) { + return createXmlParserForFile(fileName); + } + + @Override + @Nullable + public XmlPullParser createXmlParserForFile(@NonNull String fileName) { + try (FileInputStream fileStream = new FileInputStream(fileName)) { + // Read data fully to memory to be able to close the file stream. + ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream(); + ByteStreams.copy(fileStream, byteOutputStream); + KXmlParser parser = new KXmlParser(); + parser.setInput(new ByteArrayInputStream(byteOutputStream.toByteArray()), null); + return parser; + } catch (IOException | XmlPullParserException e) { + return null; + } + } + + @Override + @NonNull + public XmlPullParser createXmlParser() { + return new KXmlParser(); } @Override diff --git a/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteLayoutlibCallbackAdapter.java b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteLayoutlibCallbackAdapter.java index c8483b8e5f..e893776952 100644 --- a/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteLayoutlibCallbackAdapter.java +++ b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteLayoutlibCallbackAdapter.java @@ -139,9 +139,18 @@ public class RemoteLayoutlibCallbackAdapter implements RemoteLayoutlibCallback { } @Override - public RemoteXmlPullParser getXmlFileParser(String fileName) { + public RemoteXmlPullParser getXmlParserForPsiFile(String fileName) { try { - return RemoteXmlPullParserAdapter.create(mDelegate.getXmlFileParser(fileName)); + return RemoteXmlPullParserAdapter.create(mDelegate.getXmlParserForPsiFile(fileName)); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + + @Override + public RemoteXmlPullParser getXmlParserForFile(String fileName) { + try { + return RemoteXmlPullParserAdapter.create(mDelegate.getXmlParserForFile(fileName)); } catch (RemoteException e) { throw new RuntimeException(e); } diff --git a/remote/common/src/com/android/layout/remote/api/RemoteLayoutlibCallback.java b/remote/common/src/com/android/layout/remote/api/RemoteLayoutlibCallback.java index 49c3764444..287de56fe9 100644 --- a/remote/common/src/com/android/layout/remote/api/RemoteLayoutlibCallback.java +++ b/remote/common/src/com/android/layout/remote/api/RemoteLayoutlibCallback.java @@ -60,5 +60,15 @@ public interface RemoteLayoutlibCallback extends Remote { Path findClassPath(String name) throws RemoteException; - RemoteXmlPullParser getXmlFileParser(String fileName) throws RemoteException; + /** + * @deprecated Use {@link #getXmlParserForPsiFile}. + */ + @Deprecated + default RemoteXmlPullParser getXmlFileParser(String fileName) throws RemoteException { + return getXmlParserForPsiFile(fileName); + } + + RemoteXmlPullParser getXmlParserForPsiFile(String fileName) throws RemoteException; + + RemoteXmlPullParser getXmlParserForFile(String fileName) throws RemoteException; } |