Add support for subfolders in assets folders.
Xavier Ducrohet [Tue, 5 Mar 2013 00:39:53 +0000 (16:39 -0800)]
Change-Id: Icdae718e8bf3d4c5cadc7e709745cfa1cefc64a4

16 files changed:
builder/src/main/java/com/android/builder/internal/packaging/PackagingUtils.java
builder/src/main/java/com/android/builder/resources/AssetItem.java
builder/src/main/java/com/android/builder/resources/AssetMerger.java
builder/src/main/java/com/android/builder/resources/AssetSet.java
builder/src/main/java/com/android/builder/resources/DataSet.java
builder/src/main/java/com/android/builder/resources/ResourceSet.java
builder/src/test/java/com/android/builder/resources/AssetMergerTest.java
builder/src/test/java/com/android/builder/resources/AssetSetTest.java
builder/src/test/resources/testData/assets/baseMerge/merger.xml
builder/src/test/resources/testData/assets/baseMerge/overlay/foo/icon.png [moved from builder/src/test/resources/testData/assets/baseMerge/overlay/icon.png with 100% similarity]
builder/src/test/resources/testData/assets/baseSet/foo/foo.dat [moved from builder/src/test/resources/testData/assets/baseSet/foo.dat with 100% similarity]
builder/src/test/resources/testData/assets/baseSet/foo/icon.png [moved from builder/src/test/resources/testData/assets/baseSet/icon.png with 100% similarity]
builder/src/test/resources/testData/assets/incMergeData/basicFiles/assetOut/foo/overlay_added.png [moved from builder/src/test/resources/testData/assets/incMergeData/basicFiles/assetOut/overlay_added.png with 100% similarity]
builder/src/test/resources/testData/assets/incMergeData/basicFiles/main/foo/overlay_added.png [moved from builder/src/test/resources/testData/assets/incMergeData/basicFiles/main/overlay_added.png with 100% similarity]
builder/src/test/resources/testData/assets/incMergeData/basicFiles/merger.xml
builder/src/test/resources/testData/assets/incMergeData/basicFiles/overlay/foo/overlay_added.png [moved from builder/src/test/resources/testData/assets/incMergeData/basicFiles/overlay/overlay_added.png with 100% similarity]

index 959cc89..f781958 100644 (file)
@@ -25,6 +25,8 @@ public class PackagingUtils {
      * Checks whether a folder and its content is valid for packaging into the .apk as
      * standard Java resource.
      * @param folderName the name of the folder.
+     *
+     * @return true if the folder is valid for packaging.
      */
     public static boolean checkFolderForPackaging(String folderName) {
         return !folderName.equalsIgnoreCase("CVS") &&
index 52ca47c..000058f 100644 (file)
@@ -18,6 +18,8 @@ package com.android.builder.resources;
 
 import com.android.annotations.NonNull;
 
+import java.io.File;
+
 /**
  * An asset.
  *
@@ -27,13 +29,31 @@ import com.android.annotations.NonNull;
 class AssetItem extends DataItem<AssetFile> {
 
     /**
-     * Constructs the object with a name, type and optional value.
+     * Constructs the object with a name
      *
-     * Note that the object is not fully usable as-is. It must be added to a ResourceFile first.
+     * Note that the object is not fully usable as-is. It must be added to an AssetFile first.
      *
-     * @param name the name of the resource
+     * @param name the name of the asset
      */
     AssetItem(@NonNull String name) {
         super(name);
     }
+
+    static AssetItem create(@NonNull File sourceFolder, @NonNull File file) {
+        // compute the relative path
+        StringBuilder sb = new StringBuilder();
+        computePath(sb, file.getParentFile(), sourceFolder);
+        sb.append(file.getName());
+
+        return new AssetItem(sb.toString());
+    }
+
+    private static void computePath(StringBuilder sb, File current, File stop) {
+        if (current.equals(stop)) {
+            return;
+        }
+
+        computePath(sb, current.getParentFile(), stop);
+        sb.append(current.getName()).append('/');
+    }
 }
index 3a0bb46..bb9d54f 100644 (file)
@@ -50,11 +50,17 @@ public class AssetMerger extends DataMerger<AssetItem, AssetFile, AssetSet> {
                 public Object call() throws Exception {
                     AssetFile assetFile = item.getSource();
 
-                    File file = assetFile.getFile();
-                    String filename = file.getName();
+                    File fromFile = assetFile.getFile();
 
-                    File outFile = new File(rootFolder, filename);
-                    Files.copy(file, outFile);
+                    // the out file is computed from the item key since that includes the
+                    // relative folder.
+                    File toFile = new File(rootFolder,
+                            item.getKey().replace('/', File.separatorChar));
+
+                    // make sure the folders are created
+                    toFile.getParentFile().mkdirs();
+
+                    Files.copy(fromFile, toFile);
 
                     return null;
                 }
index c9a399e..d0f626a 100644 (file)
@@ -16,6 +16,7 @@
 
 package com.android.builder.resources;
 
+import com.android.builder.internal.packaging.PackagingUtils;
 import com.android.utils.ILogger;
 import org.w3c.dom.Attr;
 import org.w3c.dom.Node;
@@ -44,14 +45,11 @@ public class AssetSet extends DataSet<AssetItem, AssetFile> {
     }
 
     @Override
-    protected AssetFile createFileAndItems(File file, ILogger logger) {
-        // key is going to be the full filename, since you can have both
-        //     icon.png
-        // and
-        //     icon.txt
-        // in the asset folder.
+    protected AssetFile createFileAndItems(File sourceFolder, File file, ILogger logger) {
+        // key is the relative path to the sourceFolder
+        // e.g. foo/icon.png
 
-        return new AssetFile(file, new AssetItem(file.getName()));
+        return new AssetFile(file, AssetItem.create(sourceFolder, file));
     }
 
     @Override
@@ -68,21 +66,33 @@ public class AssetSet extends DataSet<AssetItem, AssetFile> {
     @Override
     protected boolean isValidSourceFile(File sourceFolder, File file) {
         // valid files are right under the source folder
-        return file.getParentFile().equals(sourceFolder);
+        File parent = file.getParentFile();
+        while (parent != null && !parent.equals(sourceFolder)) {
+            parent = parent.getParentFile();
+        }
+
+        return parent != null;
     }
 
     @Override
     protected void readSourceFolder(File sourceFolder, ILogger logger)
             throws DuplicateDataException, IOException {
-        // get the files
-        File[] files = sourceFolder.listFiles();
+        readFiles(sourceFolder, sourceFolder, logger);
+    }
+
+    private void readFiles(File sourceFolder, File folder, ILogger logger) throws IOException {
+        File[] files = folder.listFiles();
         if (files != null && files.length > 0) {
             for (File file : files) {
-                if (!file.isFile() || !checkFileForAndroidRes(file)) {
-                    continue;
+                if (file.isFile()) {
+                    if (checkFileForAndroidRes(file)) {
+                        handleNewFile(sourceFolder, file, logger);
+                    }
+                } else if (file.isDirectory()) {
+                    if (PackagingUtils.checkFolderForPackaging(folder.getName())) {
+                        readFiles(sourceFolder, file, logger);
+                    }
                 }
-
-                handleNewFile(sourceFolder, file, logger);
             }
         }
     }
index d9306f6..3b64daf 100755 (executable)
@@ -112,7 +112,8 @@ abstract class DataSet<I extends DataItem<F>, F extends DataFile<I>> implements
     protected abstract void readSourceFolder(File sourceFolder, ILogger logger)
             throws DuplicateDataException, IOException;
 
-    protected abstract F createFileAndItems(File file, ILogger logger) throws IOException;
+    protected abstract F createFileAndItems(File sourceFolder, File file, ILogger logger)
+            throws IOException;
 
 
     /**
@@ -401,7 +402,7 @@ abstract class DataSet<I extends DataItem<F>, F extends DataFile<I>> implements
 
     protected boolean handleNewFile(File sourceFolder, File file, ILogger logger)
             throws IOException {
-        F dataFile = createFileAndItems(file, logger);
+        F dataFile = createFileAndItems(sourceFolder, file, logger);
         processNewDataFile(sourceFolder, dataFile, true /*setTouched*/);
         return true;
     }
index 8f19e37..6ca0bec 100644 (file)
@@ -55,7 +55,8 @@ public class ResourceSet extends DataSet<ResourceItem, ResourceFile> {
     }
 
     @Override
-    protected ResourceFile createFileAndItems(File file, ILogger logger) throws IOException {
+    protected ResourceFile createFileAndItems(File sourceFolder, File file, ILogger logger)
+            throws IOException {
         // get the type.
         FolderData folderData = getFolderData(file.getParentFile());
 
index baa6f7d..d9e1732 100755 (executable)
@@ -37,15 +37,15 @@ public class AssetMergerTest extends BaseTestCase {
         assertEquals(5, merger.size());
     }
 
-    public void testMergedResourcesByName() throws Exception {
+    public void testMergedAssetsByName() throws Exception {
         AssetMerger merger = getAssetMerger();
 
         verifyResourceExists(merger,
-                "icon.png",
+                "foo/icon.png",
                 "icon2.png",
                 "main.xml",
                 "values.xml",
-                "foo.dat"
+                "foo/foo.dat"
         );
     }
 
@@ -140,7 +140,7 @@ public class AssetMergerTest extends BaseTestCase {
         File overlayFolder = new File(root, "overlay");
 
         // new/removed files:
-        File overlayAdded = new File(overlayFolder, "overlay_added.png");
+        File overlayAdded = new File(new File(overlayFolder, "foo"), "overlay_added.png");
         overlaySet.updateWith(overlayFolder, overlayAdded, FileStatus.NEW, logger);
         checkLogger(logger);
 
@@ -175,7 +175,7 @@ public class AssetMergerTest extends BaseTestCase {
         assertTrue(removedItem.get(0).isRemoved());
 
         // check new overlay: two objects, last one is TOUCHED
-        List<AssetItem> overlayAddedItem = mergedMap.get("overlay_added.png");
+        List<AssetItem> overlayAddedItem = mergedMap.get("foo/overlay_added.png");
         assertEquals(2, overlayAddedItem.size());
         AssetItem newOverlay0 = overlayAddedItem.get(0);
         assertTrue(newOverlay0.isWritten());
@@ -208,7 +208,7 @@ public class AssetMergerTest extends BaseTestCase {
         checkImageColor(new File(resFolder, "touched.png"), (int) 0xFF00FF00);
         checkImageColor(new File(resFolder, "added.png"), (int) 0xFF00FF00);
         checkImageColor(new File(resFolder, "overlay_removed.png"), (int) 0xFF00FF00);
-        checkImageColor(new File(resFolder, "overlay_added.png"), (int) 0xFF00FF00);
+        checkImageColor(new File(new File(resFolder, "foo"), "overlay_added.png"), (int) 0xFF00FF00);
 
         // also check the removed file is not there.
         assertFalse(new File(resFolder, "removed.png").isFile());
index 2c70dbc..cd4edd2 100644 (file)
@@ -34,8 +34,8 @@ public class AssetSetTest extends BaseTestCase {
         AssetSet assetSet = getBaseAssetSet();
 
         verifyResourceExists(assetSet,
-                "foo.dat",
-                "icon.png",
+                "foo/foo.dat",
+                "foo/icon.png",
                 "main.xml",
                 "values.xml"
         );
index c82364a..dca3713 100644 (file)
@@ -2,16 +2,16 @@
 <merger xmlns:ns1="urn:oasis:names:tc:xliff:document:1.2">
     <dataSet config="main">
         <source path="$TOP$$SEP$..$SEP$baseSet">
-            <file name="icon.png" path="$TOP$$SEP$..$SEP$baseSet$SEP$icon.png" />
+            <file name="foo/icon.png" path="$TOP$$SEP$..$SEP$baseSet$SEP$foo$SEP$icon.png" />
             <file name="main.xml" path="$TOP$$SEP$..$SEP$baseSet$SEP$main.xml" />
-            <file name="foo.dat" path="$TOP$$SEP$..$SEP$baseSet$SEP$foo.dat" />
+            <file name="foo/foo.dat" path="$TOP$$SEP$..$SEP$baseSet$SEP$foo$SEP$foo.dat" />
             <file name="value.xml" path="$TOP$$SEP$..$SEP$baseSet$SEP$value.xml" />
         </source>
     </dataSet>
     <dataSet config="overlay">
         <source path="$TOP$$SEP$overlay">
             <file name="icon2.png" path="$TOP$$SEP$overlay$SEP$icon2.png" />
-            <file name="icon.png" path="$TOP$$SEP$overlay$SEP$icon.png" />
+            <file name="foo/icon.png" path="$TOP$$SEP$overlay$SEP$foo$SEP$icon.png" />
         </source>
     </dataSet>
 </merger>
index 029155e2c1a0455e15334b4565e53e59eaeb4366..8edb8b31fff0c1df51dd245f9916507e026d3a64 100644 (file)
@@ -6,7 +6,7 @@
             <file name="touched.png" path="$TOP$$SEP$main$SEP$touched.png" />
             <file name="removed.png" path="$TOP$$SEP$main$SEP$removed.png" />
             <file name="overlay_removed.png" path="$TOP$$SEP$main$SEP$overlay_removed.png" />
-            <file name="overlay_added.png" path="$TOP$$SEP$main$SEP$overlay_added.png" />
+            <file name="foo/overlay_added.png" path="$TOP$$SEP$main$SEP$foo$SEP$overlay_added.png" />
         </source>
     </dataSet>
     <dataSet config="overlay">