Merge "Optimize layout rendering in layoutlib" into klp-dev
diff --git a/tools/layoutlib/Android.mk b/tools/layoutlib/Android.mk
index ed497a5..1fa9615 100644
--- a/tools/layoutlib/Android.mk
+++ b/tools/layoutlib/Android.mk
@@ -33,6 +33,8 @@
 
 built_ext_dep := $(call java-lib-deps,ext)
 built_ext_classes := $(call java-lib-files,ext)
+built_ext_data := $(call intermediates-dir-for, \
+			JAVA_LIBRARIES,ext,,COMMON)/javalib.jar
 
 built_layoutlib_create_jar := $(call intermediates-dir-for, \
 			JAVA_LIBRARIES,layoutlib_create,HOST)/javalib.jar
@@ -60,7 +62,8 @@
 	             $@ \
 	             $(built_core_classes) \
 	             $(built_framework_classes) \
-	             $(built_ext_classes)
+	             $(built_ext_classes) \
+	             $(built_ext_data)
 	$(hide) ls -l $(built_framework_classes)
 
 
diff --git a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
index 62d0a0d..e1b3f92 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
@@ -20,6 +20,7 @@
 import java.awt.Graphics2D;
 import java.awt.font.FontRenderContext;
 import java.awt.font.GlyphVector;
+import java.awt.geom.Rectangle2D;
 import java.util.LinkedList;
 import java.util.List;
 
@@ -27,6 +28,7 @@
 import com.ibm.icu.lang.UScriptRun;
 
 import android.graphics.Paint_Delegate.FontInfo;
+import android.graphics.RectF;;
 
 /**
  * Render the text by breaking it into various scripts and using the right font for each script.
@@ -50,9 +52,11 @@
         }
     }
 
-    /* package */ Graphics2D graphics;
-    /* package */ Paint_Delegate paint;
-    /* package */ char[] text;
+    private Graphics2D graphics;
+    private Paint_Delegate paint;
+    private char[] text;
+    // Bounds of the text drawn so far.
+    private RectF bounds;
 
     /**
      * @param graphics May be null.
@@ -82,56 +86,54 @@
      * @param x The x-coordinate of the left edge of where the text should be drawn on the given
      *            graphics.
      * @param y The y-coordinate at which to draw the text on the given graphics.
-     * @return The x-coordinate of the right edge of the drawn text. In other words,
-     *            x + the width of the text.
+     * @return A rectangle specifying the bounds of the text drawn.
      */
-    /* package */ float renderText(int start, int limit, boolean isRtl, float advances[],
+    /* package */ RectF renderText(int start, int limit, boolean isRtl, float advances[],
             int advancesIndex, boolean draw, float x, float y) {
         // We break the text into scripts and then select font based on it and then render each of
         // the script runs.
+        bounds = new RectF(x, y, x, y);
         for (ScriptRun run : getScriptRuns(text, start, limit, isRtl, paint.getFonts())) {
             int flag = Font.LAYOUT_NO_LIMIT_CONTEXT | Font.LAYOUT_NO_START_CONTEXT;
             flag |= isRtl ? Font.LAYOUT_RIGHT_TO_LEFT : Font.LAYOUT_LEFT_TO_RIGHT;
-            x = renderScript(run.start, run.limit, run.font, flag, advances, advancesIndex, draw,
-                    x, y);
+            renderScript(run.start, run.limit, run.font, flag, advances, advancesIndex, draw);
             advancesIndex += run.limit - run.start;
         }
-        return x;
+        return bounds;
     }
 
     /**
-     * Render a script run. Use the preferred font to render as much as possible. This also
-     * implements a fallback mechanism to render characters that cannot be drawn using the
-     * preferred font.
-     *
-     * @return x + width of the text drawn.
+     * Render a script run to the right of the bounds passed. Use the preferred font to render as
+     * much as possible. This also implements a fallback mechanism to render characters that cannot
+     * be drawn using the preferred font.
      */
-    private float renderScript(int start, int limit, FontInfo preferredFont, int flag,
-            float advances[], int advancesIndex, boolean draw, float x, float y) {
+    private void renderScript(int start, int limit, FontInfo preferredFont, int flag,
+            float advances[], int advancesIndex, boolean draw) {
         List<FontInfo> fonts = paint.getFonts();
         if (fonts == null || preferredFont == null) {
-            return x;
+            return;
         }
 
         while (start < limit) {
             boolean foundFont = false;
             int canDisplayUpTo = preferredFont.mFont.canDisplayUpTo(text, start, limit);
             if (canDisplayUpTo == -1) {
-                return render(start, limit, preferredFont, flag, advances, advancesIndex, draw,
-                        x, y);
+                // We can draw all characters in the text.
+                render(start, limit, preferredFont, flag, advances, advancesIndex, draw);
+                return;
             } else if (canDisplayUpTo > start) { // can draw something
-                x = render(start, canDisplayUpTo, preferredFont, flag, advances, advancesIndex,
-                        draw, x, y);
+                render(start, canDisplayUpTo, preferredFont, flag, advances, advancesIndex, draw);
                 advancesIndex += canDisplayUpTo - start;
                 start = canDisplayUpTo;
             }
 
+            // The current character cannot be drawn with the preferred font. Cycle through all the
+            // fonts to check which one can draw it.
             int charCount = Character.isHighSurrogate(text[start]) ? 2 : 1;
             for (FontInfo font : fonts) {
                 canDisplayUpTo = font.mFont.canDisplayUpTo(text, start, start + charCount);
                 if (canDisplayUpTo == -1) {
-                    x = render(start, start+charCount, font, flag, advances, advancesIndex, draw,
-                            x, y);
+                    render(start, start+charCount, font, flag, advances, advancesIndex, draw);
                     start += charCount;
                     advancesIndex += charCount;
                     foundFont = true;
@@ -143,22 +145,20 @@
                 // probably appear as a box or a blank space. We could, probably, use some
                 // heuristics and break the character into the base character and diacritics and
                 // then draw it, but it's probably not worth the effort.
-                x = render(start, start + charCount, preferredFont, flag, advances, advancesIndex,
-                        draw, x, y);
+                render(start, start + charCount, preferredFont, flag, advances, advancesIndex,
+                        draw);
                 start += charCount;
                 advancesIndex += charCount;
             }
         }
-        return x;
     }
 
     /**
-     * Render the text with the given font.
+     * Render the text with the given font to the right of the bounds passed.
      */
-    private float render(int start, int limit, FontInfo font, int flag, float advances[],
-            int advancesIndex, boolean draw, float x, float y) {
+    private void render(int start, int limit, FontInfo font, int flag, float advances[],
+            int advancesIndex, boolean draw) {
 
-        float totalAdvance = 0;
         // Since the metrics don't have anti-aliasing set, we create a new FontRenderContext with
         // the anti-aliasing set.
         FontRenderContext f = font.mMetrics.getFontRenderContext();
@@ -173,12 +173,29 @@
                 int adv_idx = advancesIndex + ci[i];
                 advances[adv_idx] += adv;
             }
-            totalAdvance += adv;
         }
         if (draw && graphics != null) {
-            graphics.drawGlyphVector(gv, x, y);
+            graphics.drawGlyphVector(gv, bounds.right, bounds.bottom);
         }
-        return x + totalAdvance;
+        Rectangle2D awtBounds = gv.getVisualBounds();
+        RectF visualBounds = awtRectToAndroidRect(awtBounds, bounds.right, bounds.bottom);
+        // If the width of the bounds is zero, no text has been drawn yet. Hence, use the
+        // coordinates from the bounds as an offset only.
+        if (Math.abs(bounds.right - bounds.left) == 0) {
+            bounds = visualBounds;
+        } else {
+            bounds.union(visualBounds);
+        }
+    }
+
+    private RectF awtRectToAndroidRect(Rectangle2D awtRec, float offsetX, float offsetY) {
+        float left = (float) awtRec.getX();
+        float top = (float) awtRec.getY();
+        float right = (float) (left + awtRec.getWidth());
+        float bottom = (float) (top + awtRec.getHeight());
+        RectF androidRect = new RectF(left, top, right, bottom);
+        androidRect.offset(offsetX, offsetY);
+        return androidRect;
     }
 
     // --- Static helper methods ---
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index 62b47bd..10ad0a3 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -990,7 +990,8 @@
                 int limit = index + count;
                 boolean isRtl = flags == Canvas.DIRECTION_RTL;
                 if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) {
-                    float m = paintDelegate.measureText(text, index, count, isRtl);
+                    RectF bounds = paintDelegate.measureText(text, index, count, isRtl);
+                    float m = bounds.right - bounds.left;
                     if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) {
                         x -= m / 2;
                     } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) {
diff --git a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
index b5134630..a79ec8f 100644
--- a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
@@ -176,7 +176,7 @@
     /*package*/ static void nativeDraw(int canvas_instance, RectF loc, int bitmap_instance,
             int chunk, int paint_instance_or_null, int destDensity, int srcDensity) {
         draw(canvas_instance,
-                (int) loc.left, (int) loc.top, (int) loc.width(), (int) loc.height(),
+                (int) loc.left, (int) loc.top, (int) loc.right, (int) loc.bottom,
                 bitmap_instance, chunk, paint_instance_or_null,
                 destDensity, srcDensity);
     }
@@ -185,7 +185,7 @@
     /*package*/ static void nativeDraw(int canvas_instance, Rect loc, int bitmap_instance,
             int chunk, int paint_instance_or_null, int destDensity, int srcDensity) {
         draw(canvas_instance,
-                loc.left, loc.top, loc.width(), loc.height(),
+                loc.left, loc.top, loc.right, loc.bottom,
                 bitmap_instance, chunk, paint_instance_or_null,
                 destDensity, srcDensity);
     }
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
index 41953ed..4ad1a17 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
@@ -575,7 +575,8 @@
             return 0;
         }
 
-        return delegate.measureText(text, index, count, isRtl(bidiFlags));
+        RectF bounds = delegate.measureText(text, index, count, isRtl(bidiFlags));
+        return bounds.right - bounds.left;
     }
 
     @LayoutlibDelegate
@@ -614,7 +615,8 @@
             }
 
             // measure from start to end
-            float res = delegate.measureText(text, start, end - start + 1, isRtl(bidiFlags));
+            RectF bounds = delegate.measureText(text, start, end - start + 1, isRtl(bidiFlags));
+            float res = bounds.right - bounds.left;
 
             if (measuredWidth != null) {
                 measuredWidth[measureIndex] = res;
@@ -991,8 +993,9 @@
         boolean isRtl = isRtl(flags);
 
         int limit = index + count;
-        return new BidiRenderer(null, delegate, text).renderText(
+        RectF bounds = new BidiRenderer(null, delegate, text).renderText(
                 index, limit, isRtl, advances, advancesIndex, false, 0, 0);
+        return bounds.right - bounds.left;
     }
 
     @LayoutlibDelegate
@@ -1058,9 +1061,7 @@
         if (delegate == null || delegate.mFonts == null || delegate.mFonts.size() == 0) {
             return;
         }
-        int w = (int) delegate.measureText(text, index, count, isRtl(bidiFlags));
-        int h= delegate.getFonts().get(0).mMetrics.getHeight();
-        bounds.set(0, 0, w, h);
+        delegate.measureText(text, index, count, isRtl(bidiFlags)).roundOut(bounds);
     }
 
     @LayoutlibDelegate
@@ -1154,7 +1155,7 @@
         }
     }
 
-    /*package*/ float measureText(char[] text, int index, int count, boolean isRtl) {
+    /*package*/ RectF measureText(char[] text, int index, int count, boolean isRtl) {
         return new BidiRenderer(null, this, text).renderText(
                 index, index + count, isRtl, null, 0, false, 0, 0);
     }
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
index 1572a40..9a31705 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
@@ -29,6 +29,7 @@
 import org.objectweb.asm.signature.SignatureVisitor;
 
 import java.io.IOException;
+import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.List;
@@ -60,6 +61,9 @@
     private final String[] mIncludeGlobs;
     /** The set of classes to exclude.*/
     private final Set<String> mExcludedClasses;
+    /** Glob patterns of files to keep as is. */
+    private final String[] mIncludeFileGlobs;
+    /** Copy these files into the output as is. */
 
     /**
      * Creates a new analyzer.
@@ -70,15 +74,19 @@
      * @param deriveFrom Keep all classes that derive from these one (these included).
      * @param includeGlobs Glob patterns of classes to keep, e.g. "com.foo.*"
      *        ("*" does not matches dots whilst "**" does, "." and "$" are interpreted as-is)
+     * @param includeFileGlobs Glob patterns of files which are kept as is. This is only for files
+     *        not ending in .class.
      */
     public AsmAnalyzer(Log log, List<String> osJarPath, AsmGenerator gen,
-            String[] deriveFrom, String[] includeGlobs, Set<String> excludeClasses) {
+            String[] deriveFrom, String[] includeGlobs, Set<String> excludeClasses,
+            String[] includeFileGlobs) {
         mLog = log;
         mGen = gen;
         mOsSourceJar = osJarPath != null ? osJarPath : new ArrayList<String>();
         mDeriveFrom = deriveFrom != null ? deriveFrom : new String[0];
         mIncludeGlobs = includeGlobs != null ? includeGlobs : new String[0];
         mExcludedClasses = excludeClasses;
+        mIncludeFileGlobs = includeFileGlobs != null ? includeFileGlobs : new String[0];
     }
 
     /**
@@ -86,7 +94,11 @@
      * Fills the generator with classes & dependencies found.
      */
     public void analyze() throws IOException, LogAbortException {
-        Map<String, ClassReader> zipClasses = parseZip(mOsSourceJar);
+
+        TreeMap<String, ClassReader> zipClasses = new TreeMap<String, ClassReader>();
+        Map<String, InputStream> filesFound = new TreeMap<String, InputStream>();
+
+        parseZip(mOsSourceJar, zipClasses, filesFound);
         mLog.info("Found %d classes in input JAR%s.", zipClasses.size(),
                 mOsSourceJar.size() > 1 ? "s" : "");
 
@@ -96,15 +108,29 @@
         if (mGen != null) {
             mGen.setKeep(found);
             mGen.setDeps(deps);
+            mGen.setCopyFiles(filesFound);
         }
     }
 
     /**
-     * Parses a JAR file and returns a list of all classes founds using a map
-     * class name => ASM ClassReader. Class names are in the form "android.view.View".
+     * Parses a JAR file and adds all the classes found to <code>classes</code>
+     * and all other files to <code>filesFound</code>.
+     *
+     * @param classes The map of class name => ASM ClassReader. Class names are
+     *                in the form "android.view.View".
+     * @param fileFound The map of file name => InputStream. The file name is
+     *                  in the form "android/data/dataFile".
      */
-    Map<String,ClassReader> parseZip(List<String> jarPathList) throws IOException {
-        TreeMap<String, ClassReader> classes = new TreeMap<String, ClassReader>();
+    void parseZip(List<String> jarPathList, Map<String, ClassReader> classes,
+            Map<String, InputStream> filesFound) throws IOException {
+        if (classes == null || filesFound == null) {
+            return;
+        }
+
+        Pattern[] includeFilePatterns = new Pattern[mIncludeFileGlobs.length];
+        for (int i = 0; i < mIncludeFileGlobs.length; ++i) {
+            includeFilePatterns[i] = getPatternFromGlob(mIncludeFileGlobs[i]);
+        }
 
         for (String jarPath : jarPathList) {
             ZipFile zip = new ZipFile(jarPath);
@@ -116,11 +142,17 @@
                     ClassReader cr = new ClassReader(zip.getInputStream(entry));
                     String className = classReaderToClassName(cr);
                     classes.put(className, cr);
+                } else {
+                    for (int i = 0; i < includeFilePatterns.length; ++i) {
+                        if (includeFilePatterns[i].matcher(entry.getName()).matches()) {
+                            filesFound.put(entry.getName(), zip.getInputStream(entry));
+                            break;
+                        }
+                    }
                 }
             }
         }
 
-        return classes;
     }
 
     /**
@@ -202,7 +234,19 @@
      */
     void findGlobs(String globPattern, Map<String, ClassReader> zipClasses,
             Map<String, ClassReader> inOutFound) throws LogAbortException {
-        // transforms the glob pattern in a regexp:
+
+        Pattern regexp = getPatternFromGlob(globPattern);
+
+        for (Entry<String, ClassReader> entry : zipClasses.entrySet()) {
+            String class_name = entry.getKey();
+            if (regexp.matcher(class_name).matches()) {
+                findClass(class_name, zipClasses, inOutFound);
+            }
+        }
+    }
+
+    Pattern getPatternFromGlob(String globPattern) {
+     // transforms the glob pattern in a regexp:
         // - escape "." with "\."
         // - replace "*" by "[^.]*"
         // - escape "$" with "\$"
@@ -216,14 +260,7 @@
         globPattern = globPattern.replaceAll("@", ".*");
         globPattern += "$";
 
-        Pattern regexp = Pattern.compile(globPattern);
-
-        for (Entry<String, ClassReader> entry : zipClasses.entrySet()) {
-            String class_name = entry.getKey();
-            if (regexp.matcher(class_name).matches()) {
-                findClass(class_name, zipClasses, inOutFound);
-            }
-        }
+        return Pattern.compile(globPattern);
     }
 
     /**
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 b102561..207d8ae 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
@@ -20,6 +20,7 @@
 import org.objectweb.asm.ClassVisitor;
 import org.objectweb.asm.ClassWriter;
 
+import java.io.ByteArrayOutputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -52,6 +53,8 @@
     private Map<String, ClassReader> mKeep;
     /** All dependencies that must be completely stubbed. */
     private Map<String, ClassReader> mDeps;
+    /** All files that are to be copied as-is. */
+    private Map<String, InputStream> mCopyFiles;
     /** Counter of number of classes renamed during transform. */
     private int mRenameCount;
     /** FQCN Names of the classes to rename: map old-FQCN => new-FQCN */
@@ -195,6 +198,11 @@
         mDeps = deps;
     }
 
+    /** Sets the map of files to output as-is. */
+    public void setCopyFiles(Map<String, InputStream> copyFiles) {
+        mCopyFiles = copyFiles;
+    }
+
     /** Gets the map of classes to output as-is, except if they have native methods */
     public Map<String, ClassReader> getKeep() {
         return mKeep;
@@ -205,6 +213,11 @@
         return mDeps;
     }
 
+    /** Gets the map of files to output as-is. */
+    public Map<String, InputStream> getCopyFiles() {
+        return mCopyFiles;
+    }
+
     /** Generates the final JAR */
     public void generate() throws FileNotFoundException, IOException {
         TreeMap<String, byte[]> all = new TreeMap<String, byte[]>();
@@ -232,6 +245,15 @@
             all.put(name, b);
         }
 
+        for (Entry<String, InputStream> entry : mCopyFiles.entrySet()) {
+            try {
+                byte[] b = inputStreamToByteArray(entry.getValue());
+                all.put(entry.getKey(), b);
+            } catch (IOException e) {
+                // Ignore.
+            }
+
+        }
         mLog.info("# deps classes: %d", mDeps.size());
         mLog.info("# keep classes: %d", mKeep.size());
         mLog.info("# renamed     : %d", mRenameCount);
@@ -381,4 +403,13 @@
         return cv.hasNativeMethods();
     }
 
+    private byte[] inputStreamToByteArray(InputStream is) throws IOException {
+        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+        byte[] data = new byte[8192];  // 8KB
+        int n;
+        while ((n = is.read(data, 0, data.length)) != -1) {
+            buffer.write(data, 0, n);
+        }
+        return buffer.toByteArray();
+    }
 }
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
index ee501d2..a79fba1 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
@@ -115,7 +115,10 @@
                         "android.database.ContentObserver", // for Digital clock
                         "com.android.i18n.phonenumbers.*",  // for TextView with autolink attribute
                     },
-                    excludeClasses);
+                    excludeClasses,
+                    new String[] {
+                        "com/android/i18n/phonenumbers/data/*",
+                    });
             aa.analyze();
             agen.generate();
 
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
index 005fc9d..7ec0d38 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
@@ -29,6 +29,7 @@
 import org.objectweb.asm.ClassReader;
 
 import java.io.IOException;
+import java.io.InputStream;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -55,8 +56,10 @@
 
         Set<String> excludeClasses = new HashSet<String>(1);
         excludeClasses.add("java.lang.JavaClass");
-        mAa = new AsmAnalyzer(mLog, mOsJarPath, null /* gen */,
-                null /* deriveFrom */, null /* includeGlobs */, excludeClasses);
+
+        String[] includeFiles = new String[]{"mock_android/data/data*"};
+        mAa = new AsmAnalyzer(mLog, mOsJarPath, null /* gen */, null /* deriveFrom */,
+                null /* includeGlobs */, excludeClasses, includeFiles);
     }
 
     @After
@@ -65,7 +68,11 @@
 
     @Test
     public void testParseZip() throws IOException {
-        Map<String, ClassReader> map = mAa.parseZip(mOsJarPath);
+
+        Map<String, ClassReader> map = new TreeMap<String, ClassReader>();
+        Map<String, InputStream> filesFound = new TreeMap<String, InputStream>();
+
+        mAa.parseZip(mOsJarPath, map, filesFound);
 
         assertArrayEquals(new String[] {
                 "java.lang.JavaClass",
@@ -86,11 +93,17 @@
                 "mock_android.widget.TableLayout$LayoutParams"
             },
             map.keySet().toArray());
+        assertArrayEquals(new String[] {"mock_android/data/dataFile"},
+            filesFound.keySet().toArray());
     }
 
     @Test
     public void testFindClass() throws IOException, LogAbortException {
-        Map<String, ClassReader> zipClasses = mAa.parseZip(mOsJarPath);
+
+        Map<String, ClassReader> zipClasses = new TreeMap<String, ClassReader>();
+        Map<String, InputStream> filesFound = new TreeMap<String, InputStream>();
+
+        mAa.parseZip(mOsJarPath, zipClasses, filesFound);
         TreeMap<String, ClassReader> found = new TreeMap<String, ClassReader>();
 
         ClassReader cr = mAa.findClass("mock_android.view.ViewGroup$LayoutParams",
@@ -105,7 +118,11 @@
 
     @Test
     public void testFindGlobs() throws IOException, LogAbortException {
-        Map<String, ClassReader> zipClasses = mAa.parseZip(mOsJarPath);
+
+        Map<String, ClassReader> zipClasses = new TreeMap<String, ClassReader>();
+        Map<String, InputStream> filesFound = new TreeMap<String, InputStream>();
+
+        mAa.parseZip(mOsJarPath, zipClasses, filesFound);
         TreeMap<String, ClassReader> found = new TreeMap<String, ClassReader>();
 
         // this matches classes, a package match returns nothing
@@ -164,7 +181,11 @@
 
     @Test
     public void testFindClassesDerivingFrom() throws LogAbortException, IOException {
-        Map<String, ClassReader> zipClasses = mAa.parseZip(mOsJarPath);
+
+        Map<String, ClassReader> zipClasses = new TreeMap<String, ClassReader>();
+        Map<String, InputStream> filesFound = new TreeMap<String, InputStream>();
+
+        mAa.parseZip(mOsJarPath, zipClasses, filesFound);
         TreeMap<String, ClassReader> found = new TreeMap<String, ClassReader>();
 
         mAa.findClassesDerivingFrom("mock_android.view.View", zipClasses, found);
@@ -186,7 +207,11 @@
 
     @Test
     public void testDependencyVisitor() throws IOException, LogAbortException {
-        Map<String, ClassReader> zipClasses = mAa.parseZip(mOsJarPath);
+
+        Map<String, ClassReader> zipClasses = new TreeMap<String, ClassReader>();
+        Map<String, InputStream> filesFound = new TreeMap<String, InputStream>();
+
+        mAa.parseZip(mOsJarPath, zipClasses, filesFound);
         TreeMap<String, ClassReader> keep = new TreeMap<String, ClassReader>();
         TreeMap<String, ClassReader> new_keep = new TreeMap<String, ClassReader>();
         TreeMap<String, ClassReader> in_deps = new TreeMap<String, ClassReader>();
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
index 8a27173..0dbc238 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
@@ -33,6 +33,7 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.io.InputStream;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Enumeration;
@@ -131,7 +132,8 @@
                 new String[] {        // include classes
                     "**"
                 },
-                new HashSet<String>(0) /* excluded classes */);
+                new HashSet<String>(0) /* excluded classes */,
+                new String[]{} /* include files */);
         aa.analyze();
         agen.generate();
 
@@ -195,10 +197,15 @@
                 new String[] {        // include classes
                     "**"
                 },
-                new HashSet<String>(1));
+                new HashSet<String>(1),
+                new String[] {        /* include files */
+                    "mock_android/data/data*"
+                });
         aa.analyze();
         agen.generate();
-        Map<String, ClassReader> output = parseZip(mOsDestJar);
+        Map<String, ClassReader> output = new TreeMap<String, ClassReader>();
+        Map<String, InputStream> filesFound = new TreeMap<String, InputStream>();
+        parseZip(mOsDestJar, output, filesFound);
         boolean injectedClassFound = false;
         for (ClassReader cr: output.values()) {
             TestClassVisitor cv = new TestClassVisitor();
@@ -206,10 +213,13 @@
             injectedClassFound |= cv.mInjectedClassFound;
         }
         assertTrue(injectedClassFound);
+        assertArrayEquals(new String[] {"mock_android/data/dataFile"},
+                filesFound.keySet().toArray());
     }
 
-    private Map<String,ClassReader> parseZip(String jarPath) throws IOException {
-        TreeMap<String, ClassReader> classes = new TreeMap<String, ClassReader>();
+    private void parseZip(String jarPath,
+            Map<String, ClassReader> classes,
+            Map<String, InputStream> filesFound) throws IOException {
 
             ZipFile zip = new ZipFile(jarPath);
             Enumeration<? extends ZipEntry> entries = zip.entries();
@@ -220,10 +230,11 @@
                     ClassReader cr = new ClassReader(zip.getInputStream(entry));
                     String className = classReaderToClassName(cr);
                     classes.put(className, cr);
+                } else {
+                    filesFound.put(entry.getName(), zip.getInputStream(entry));
                 }
             }
 
-        return classes;
     }
 
     private String classReaderToClassName(ClassReader classReader) {
diff --git a/tools/layoutlib/create/tests/data/mock_android.jar b/tools/layoutlib/create/tests/data/mock_android.jar
index 60d8efb..8dd0481 100644
--- a/tools/layoutlib/create/tests/data/mock_android.jar
+++ b/tools/layoutlib/create/tests/data/mock_android.jar
Binary files differ
diff --git a/tools/layoutlib/create/tests/mock_data/mock_android/data/anotherDataFile b/tools/layoutlib/create/tests/mock_data/mock_android/data/anotherDataFile
new file mode 100644
index 0000000..ab29fbe
--- /dev/null
+++ b/tools/layoutlib/create/tests/mock_data/mock_android/data/anotherDataFile
@@ -0,0 +1 @@
+A simple data file that should *not* be copied to the output jar.
\ No newline at end of file
diff --git a/tools/layoutlib/create/tests/mock_data/mock_android/data/dataFile b/tools/layoutlib/create/tests/mock_data/mock_android/data/dataFile
new file mode 100644
index 0000000..9b01893
--- /dev/null
+++ b/tools/layoutlib/create/tests/mock_data/mock_android/data/dataFile
@@ -0,0 +1 @@
+A simple data file that should be copied to the output jar unchanged.
\ No newline at end of file