]> git.saurik.com Git - android/aapt.git/commitdiff
Merge "Fix for issue 9656 - custom xml attributes in Android Libraries"
authorJean-Baptiste Queru <jbq@google.com>
Thu, 23 Feb 2012 20:38:12 +0000 (12:38 -0800)
committerandroid code review <noreply-gerritcodereview@google.com>
Thu, 23 Feb 2012 20:38:14 +0000 (12:38 -0800)
32 files changed:
AaptAssets.cpp
AaptAssets.h
Android.mk
Bundle.h
CacheUpdater.h [new file with mode: 0644]
Command.cpp
CrunchCache.cpp [new file with mode: 0644]
CrunchCache.h [new file with mode: 0644]
DirectoryWalker.h [new file with mode: 0644]
FileFinder.cpp [new file with mode: 0644]
FileFinder.h [new file with mode: 0644]
Images.cpp
Images.h
Main.cpp
Main.h
NOTICE [new file with mode: 0644]
Package.cpp
Resource.cpp
ResourceFilter.cpp [new file with mode: 0644]
ResourceFilter.h [new file with mode: 0644]
ResourceTable.cpp
ResourceTable.h
StringPool.cpp
XMLNode.cpp
ZipEntry.cpp
ZipFile.cpp
ZipFile.h
tests/CrunchCache_test.cpp [new file with mode: 0644]
tests/FileFinder_test.cpp [new file with mode: 0644]
tests/MockCacheUpdater.h [new file with mode: 0644]
tests/MockDirectoryWalker.h [new file with mode: 0644]
tests/MockFileFinder.h [new file with mode: 0644]

index 2b2ec7b3e9a7c1c6ec458c4e5a60c279ebd106ba..3d6537a9013e41725bbeb4cfe2cca31a353973dc 100644 (file)
@@ -3,6 +3,7 @@
 //
 
 #include "AaptAssets.h"
+#include "ResourceFilter.h"
 #include "Main.h"
 
 #include <utils/misc.h>
@@ -16,6 +17,8 @@ static const char* kDefaultLocale = "default";
 static const char* kWildcardName = "any";
 static const char* kAssetDir = "assets";
 static const char* kResourceDir = "res";
+static const char* kValuesDir = "values";
+static const char* kMipmapDir = "mipmap";
 static const char* kInvalidChars = "/\\:";
 static const size_t kMaxAssetFileName = 100;
 
@@ -142,6 +145,27 @@ AaptGroupEntry::parseNamePart(const String8& part, int* axis, uint32_t* value)
         return 0;
     }
 
+    // smallest screen dp width
+    if (getSmallestScreenWidthDpName(part.string(), &config)) {
+        *axis = AXIS_SMALLESTSCREENWIDTHDP;
+        *value = config.smallestScreenWidthDp;
+        return 0;
+    }
+
+    // screen dp width
+    if (getScreenWidthDpName(part.string(), &config)) {
+        *axis = AXIS_SCREENWIDTHDP;
+        *value = config.screenWidthDp;
+        return 0;
+    }
+
+    // screen dp height
+    if (getScreenHeightDpName(part.string(), &config)) {
+        *axis = AXIS_SCREENHEIGHTDP;
+        *value = config.screenHeightDp;
+        return 0;
+    }
+
     // screen layout size
     if (getScreenLayoutSizeName(part.string(), &config)) {
         *axis = AXIS_SCREENLAYOUTSIZE;
@@ -236,14 +260,74 @@ AaptGroupEntry::parseNamePart(const String8& part, int* axis, uint32_t* value)
     return 1;
 }
 
+uint32_t
+AaptGroupEntry::getConfigValueForAxis(const ResTable_config& config, int axis)
+{
+    switch (axis) {
+        case AXIS_MCC:
+            return config.mcc;
+        case AXIS_MNC:
+            return config.mnc;
+        case AXIS_LANGUAGE:
+            return (((uint32_t)config.country[1]) << 24) | (((uint32_t)config.country[0]) << 16)
+                | (((uint32_t)config.language[1]) << 8) | (config.language[0]);
+        case AXIS_SCREENLAYOUTSIZE:
+            return config.screenLayout&ResTable_config::MASK_SCREENSIZE;
+        case AXIS_ORIENTATION:
+            return config.orientation;
+        case AXIS_UIMODETYPE:
+            return (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
+        case AXIS_UIMODENIGHT:
+            return (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
+        case AXIS_DENSITY:
+            return config.density;
+        case AXIS_TOUCHSCREEN:
+            return config.touchscreen;
+        case AXIS_KEYSHIDDEN:
+            return config.inputFlags;
+        case AXIS_KEYBOARD:
+            return config.keyboard;
+        case AXIS_NAVIGATION:
+            return config.navigation;
+        case AXIS_SCREENSIZE:
+            return config.screenSize;
+        case AXIS_SMALLESTSCREENWIDTHDP:
+            return config.smallestScreenWidthDp;
+        case AXIS_SCREENWIDTHDP:
+            return config.screenWidthDp;
+        case AXIS_SCREENHEIGHTDP:
+            return config.screenHeightDp;
+        case AXIS_VERSION:
+            return config.version;
+    }
+    return 0;
+}
+
+bool
+AaptGroupEntry::configSameExcept(const ResTable_config& config,
+        const ResTable_config& otherConfig, int axis)
+{
+    for (int i=AXIS_START; i<=AXIS_END; i++) {
+        if (i == axis) {
+            continue;
+        }
+        if (getConfigValueForAxis(config, i) != getConfigValueForAxis(otherConfig, i)) {
+            return false;
+        }
+    }
+    return true;
+}
+
 bool
 AaptGroupEntry::initFromDirName(const char* dir, String8* resType)
 {
+    mParamsChanged = true;
+
     Vector<String8> parts;
 
     String8 mcc, mnc, loc, layoutsize, layoutlong, orient, den;
     String8 touch, key, keysHidden, nav, navHidden, size, vers;
-    String8 uiModeType, uiModeNight;
+    String8 uiModeType, uiModeNight, smallestwidthdp, widthdp, heightdp;
 
     const char *p = dir;
     const char *q;
@@ -330,6 +414,42 @@ AaptGroupEntry::initFromDirName(const char* dir, String8* resType)
         //printf("not region: %s\n", part.string());
     }
 
+    if (getSmallestScreenWidthDpName(part.string())) {
+        smallestwidthdp = part;
+
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index];
+    } else {
+        //printf("not smallest screen width dp: %s\n", part.string());
+    }
+
+    if (getScreenWidthDpName(part.string())) {
+        widthdp = part;
+
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index];
+    } else {
+        //printf("not screen width dp: %s\n", part.string());
+    }
+
+    if (getScreenHeightDpName(part.string())) {
+        heightdp = part;
+
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index];
+    } else {
+        //printf("not screen height dp: %s\n", part.string());
+    }
+
     if (getScreenLayoutSizeName(part.string())) {
         layoutsize = part;
 
@@ -503,6 +623,9 @@ success:
     this->locale = loc;
     this->screenLayoutSize = layoutsize;
     this->screenLayoutLong = layoutlong;
+    this->smallestScreenWidthDp = smallestwidthdp;
+    this->screenWidthDp = widthdp;
+    this->screenHeightDp = heightdp;
     this->orientation = orient;
     this->uiModeType = uiModeType;
     this->uiModeNight = uiModeNight;
@@ -530,6 +653,12 @@ AaptGroupEntry::toString() const
     s += ",";
     s += this->locale;
     s += ",";
+    s += smallestScreenWidthDp;
+    s += ",";
+    s += screenWidthDp;
+    s += ",";
+    s += screenHeightDp;
+    s += ",";
     s += screenLayoutSize;
     s += ",";
     s += screenLayoutLong;
@@ -563,67 +692,117 @@ AaptGroupEntry::toDirName(const String8& resType) const
 {
     String8 s = resType;
     if (this->mcc != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += mcc;
     }
     if (this->mnc != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += mnc;
     }
     if (this->locale != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += locale;
     }
+    if (this->smallestScreenWidthDp != "") {
+        if (s.length() > 0) {
+            s += "-";
+        }
+        s += smallestScreenWidthDp;
+    }
+    if (this->screenWidthDp != "") {
+        if (s.length() > 0) {
+            s += "-";
+        }
+        s += screenWidthDp;
+    }
+    if (this->screenHeightDp != "") {
+        if (s.length() > 0) {
+            s += "-";
+        }
+        s += screenHeightDp;
+    }
     if (this->screenLayoutSize != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += screenLayoutSize;
     }
     if (this->screenLayoutLong != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += screenLayoutLong;
     }
     if (this->orientation != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += orientation;
     }
     if (this->uiModeType != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += uiModeType;
     }
     if (this->uiModeNight != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += uiModeNight;
     }
     if (this->density != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += density;
     }
     if (this->touchscreen != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += touchscreen;
     }
     if (this->keysHidden != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += keysHidden;
     }
     if (this->keyboard != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += keyboard;
     }
     if (this->navHidden != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += navHidden;
     }
     if (this->navigation != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += navigation;
     }
     if (this->screenSize != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += screenSize;
     }
     if (this->version != "") {
-        s += "-";
+        if (s.length() > 0) {
+            s += "-";
+        }
         s += version;
     }
 
@@ -835,6 +1014,11 @@ bool AaptGroupEntry::getUiModeTypeName(const char* name,
               (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
               | ResTable_config::UI_MODE_TYPE_CAR;
         return true;
+    } else if (strcmp(name, "television") == 0) {
+      if (out) out->uiMode =
+              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+              | ResTable_config::UI_MODE_TYPE_TELEVISION;
+        return true;
     }
 
     return false;
@@ -886,6 +1070,11 @@ bool AaptGroupEntry::getDensityName(const char* name,
         return true;
     }
     
+    if (strcmp(name, "tvdpi") == 0) {
+        if (out) out->density = ResTable_config::DENSITY_TV;
+        return true;
+    }
+    
     if (strcmp(name, "hdpi") == 0) {
         if (out) out->density = ResTable_config::DENSITY_HIGH;
         return true;
@@ -1039,8 +1228,7 @@ bool AaptGroupEntry::getNavigationName(const char* name,
     return false;
 }
 
-bool AaptGroupEntry::getScreenSizeName(const char* name,
-                                       ResTable_config* out)
+bool AaptGroupEntry::getScreenSizeName(const char* name, ResTable_config* out)
 {
     if (strcmp(name, kWildcardName) == 0) {
         if (out) {
@@ -1075,8 +1263,78 @@ bool AaptGroupEntry::getScreenSizeName(const char* name,
     return true;
 }
 
-bool AaptGroupEntry::getVersionName(const char* name,
-                                    ResTable_config* out)
+bool AaptGroupEntry::getSmallestScreenWidthDpName(const char* name, ResTable_config* out)
+{
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) {
+            out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
+        }
+        return true;
+    }
+
+    if (*name != 's') return false;
+    name++;
+    if (*name != 'w') return false;
+    name++;
+    const char* x = name;
+    while (*x >= '0' && *x <= '9') x++;
+    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
+    String8 xName(name, x-name);
+
+    if (out) {
+        out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
+    }
+
+    return true;
+}
+
+bool AaptGroupEntry::getScreenWidthDpName(const char* name, ResTable_config* out)
+{
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) {
+            out->screenWidthDp = out->SCREENWIDTH_ANY;
+        }
+        return true;
+    }
+
+    if (*name != 'w') return false;
+    name++;
+    const char* x = name;
+    while (*x >= '0' && *x <= '9') x++;
+    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
+    String8 xName(name, x-name);
+
+    if (out) {
+        out->screenWidthDp = (uint16_t)atoi(xName.string());
+    }
+
+    return true;
+}
+
+bool AaptGroupEntry::getScreenHeightDpName(const char* name, ResTable_config* out)
+{
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) {
+            out->screenHeightDp = out->SCREENWIDTH_ANY;
+        }
+        return true;
+    }
+
+    if (*name != 'h') return false;
+    name++;
+    const char* x = name;
+    while (*x >= '0' && *x <= '9') x++;
+    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
+    String8 xName(name, x-name);
+
+    if (out) {
+        out->screenHeightDp = (uint16_t)atoi(xName.string());
+    }
+
+    return true;
+}
+
+bool AaptGroupEntry::getVersionName(const char* name, ResTable_config* out)
 {
     if (strcmp(name, kWildcardName) == 0) {
         if (out) {
@@ -1110,6 +1368,9 @@ int AaptGroupEntry::compare(const AaptGroupEntry& o) const
     if (v == 0) v = mnc.compare(o.mnc);
     if (v == 0) v = locale.compare(o.locale);
     if (v == 0) v = vendor.compare(o.vendor);
+    if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp);
+    if (v == 0) v = screenWidthDp.compare(o.screenWidthDp);
+    if (v == 0) v = screenHeightDp.compare(o.screenHeightDp);
     if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize);
     if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong);
     if (v == 0) v = orientation.compare(o.orientation);
@@ -1126,13 +1387,21 @@ int AaptGroupEntry::compare(const AaptGroupEntry& o) const
     return v;
 }
 
-ResTable_config AaptGroupEntry::toParams() const
+const ResTable_config& AaptGroupEntry::toParams() const
 {
-    ResTable_config params;
+    if (!mParamsChanged) {
+        return mParams;
+    }
+
+    mParamsChanged = false;
+    ResTable_config& params(mParams);
     memset(&params, 0, sizeof(params));
     getMccName(mcc.string(), &params);
     getMncName(mnc.string(), &params);
     getLocaleName(locale.string(), &params);
+    getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), &params);
+    getScreenWidthDpName(screenWidthDp.string(), &params);
+    getScreenHeightDpName(screenHeightDp.string(), &params);
     getScreenLayoutSizeName(screenLayoutSize.string(), &params);
     getScreenLayoutLongName(screenLayoutLong.string(), &params);
     getOrientationName(orientation.string(), &params);
@@ -1149,7 +1418,11 @@ ResTable_config AaptGroupEntry::toParams() const
     
     // Fix up version number based on specified parameters.
     int minSdk = 0;
-    if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE)
+    if (params.smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
+            || params.screenWidthDp != ResTable_config::SCREENWIDTH_ANY
+            || params.screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
+        minSdk = SDK_HONEYCOMB_MR2;
+    } else if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE)
                 != ResTable_config::UI_MODE_TYPE_ANY
             ||  (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT)
                 != ResTable_config::UI_MODE_NIGHT_ANY) {
@@ -1236,8 +1509,7 @@ void AaptFile::clearData()
 String8 AaptFile::getPrintableSource() const
 {
     if (hasData()) {
-        String8 name(mGroupEntry.locale.string());
-        name.appendPath(mGroupEntry.vendor.string());
+        String8 name(mGroupEntry.toDirName(String8()));
         name.appendPath(mPath);
         name.append(" #generated");
         return name;
@@ -1257,6 +1529,13 @@ status_t AaptGroup::addFile(const sp<AaptFile>& file)
         return NO_ERROR;
     }
 
+#if 0
+    printf("Error adding file %s: group %s already exists in leaf=%s path=%s\n",
+            file->getSourceFile().string(),
+            file->getGroupEntry().toDirName(String8()).string(),
+            mLeaf.string(), mPath.string());
+#endif
+
     SourcePos(file->getSourceFile(), -1).error("Duplicate file.\n%s: Original is here.",
                                                getPrintableSource().string());
     return UNKNOWN_ERROR;
@@ -1267,20 +1546,23 @@ void AaptGroup::removeFile(size_t index)
        mFiles.removeItemsAt(index);
 }
 
-void AaptGroup::print() const
+void AaptGroup::print(const String8& prefix) const
 {
-    printf("  %s\n", getPath().string());
+    printf("%s%s\n", prefix.string(), getPath().string());
     const size_t N=mFiles.size();
     size_t i;
     for (i=0; i<N; i++) {
         sp<AaptFile> file = mFiles.valueAt(i);
         const AaptGroupEntry& e = file->getGroupEntry();
         if (file->hasData()) {
-            printf("      Gen: (%s) %d bytes\n", e.toString().string(),
+            printf("%s  Gen: (%s) %d bytes\n", prefix.string(), e.toDirName(String8()).string(),
                     (int)file->getSize());
         } else {
-            printf("      Src: %s\n", file->getPrintableSource().string());
+            printf("%s  Src: (%s) %s\n", prefix.string(), e.toDirName(String8()).string(),
+                    file->getPrintableSource().string());
         }
+        //printf("%s  File Group Entry: %s\n", prefix.string(),
+        //        file->getGroupEntry().toDirName(String8()).string());
     }
 }
 
@@ -1347,38 +1629,6 @@ void AaptDir::removeDir(const String8& name)
     mDirs.removeItem(name);
 }
 
-status_t AaptDir::renameFile(const sp<AaptFile>& file, const String8& newName)
-{
-       sp<AaptGroup> origGroup;
-
-       // Find and remove the given file with shear, brute force!
-       const size_t NG = mFiles.size();
-       size_t i;
-       for (i=0; origGroup == NULL && i<NG; i++) {
-               sp<AaptGroup> g = mFiles.valueAt(i);
-               const size_t NF = g->getFiles().size();
-               for (size_t j=0; j<NF; j++) {
-                       if (g->getFiles().valueAt(j) == file) {
-                               origGroup = g;
-                               g->removeFile(j);
-                               if (NF == 1) {
-                                       mFiles.removeItemsAt(i);
-                               }
-                               break;
-                       }
-               }
-       }
-
-       //printf("Renaming %s to %s\n", file->getPath().getPathName(), newName.string());
-
-       // Place the file under its new name.
-       if (origGroup != NULL) {
-               return addLeafFile(newName, file);
-       }
-
-       return NO_ERROR;
-}
-
 status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file)
 {
     sp<AaptGroup> group;
@@ -1393,10 +1643,10 @@ status_t AaptDir::addLeafFile(const String8& leafName, const sp<AaptFile>& file)
 }
 
 ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
-                            const AaptGroupEntry& kind, const String8& resType)
+                            const AaptGroupEntry& kind, const String8& resType,
+                            sp<FilePathStore>& fullResPaths)
 {
     Vector<String8> fileNames;
-
     {
         DIR* dir = NULL;
 
@@ -1419,9 +1669,14 @@ ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
             if (isHidden(srcDir.string(), entry->d_name))
                 continue;
 
-            fileNames.add(String8(entry->d_name));
+            String8 name(entry->d_name);
+            fileNames.add(name);
+            // Add fully qualified path for dependency purposes
+            // if we're collecting them
+            if (fullResPaths != NULL) {
+                fullResPaths->add(srcDir.appendPathCopy(name));
+            }
         }
-
         closedir(dir);
     }
 
@@ -1448,7 +1703,7 @@ ssize_t AaptDir::slurpFullTree(Bundle* bundle, const String8& srcDir,
                 notAdded = true;
             }
             ssize_t res = subdir->slurpFullTree(bundle, pathName, kind,
-                                                resType);
+                                                resType, fullResPaths);
             if (res < NO_ERROR) {
                 return res;
             }
@@ -1538,17 +1793,17 @@ status_t AaptDir::validate() const
     return NO_ERROR;
 }
 
-void AaptDir::print() const
+void AaptDir::print(const String8& prefix) const
 {
     const size_t ND=getDirs().size();
     size_t i;
     for (i=0; i<ND; i++) {
-        getDirs().valueAt(i)->print();
+        getDirs().valueAt(i)->print(prefix);
     }
 
     const size_t NF=getFiles().size();
     for (i=0; i<NF; i++) {
-        getFiles().valueAt(i)->print();
+        getFiles().valueAt(i)->print(prefix);
     }
 }
 
@@ -1572,6 +1827,24 @@ String8 AaptDir::getPrintableSource() const
 // =========================================================================
 // =========================================================================
 
+AaptAssets::AaptAssets()
+    : AaptDir(String8(), String8()),
+      mChanged(false), mHaveIncludedAssets(false), mRes(NULL)
+{
+}
+
+const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
+    if (mChanged) {
+    }
+    return mGroupEntries;
+}
+
+status_t AaptAssets::addFile(const String8& name, const sp<AaptGroup>& file)
+{
+    mChanged = true;
+    return AaptDir::addFile(name, file);
+}
+
 sp<AaptFile> AaptAssets::addFile(
         const String8& filePath, const AaptGroupEntry& entry,
         const String8& srcDir, sp<AaptGroup>* outGroup,
@@ -1680,7 +1953,7 @@ ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
         sp<AaptDir> assetAaptDir = makeDir(String8(kAssetDir));
         AaptGroupEntry group;
         count = assetAaptDir->slurpFullTree(bundle, assetRoot, group,
-                                            String8());
+                                            String8(), mFullAssetPaths);
         if (count < 0) {
             totalCount = count;
             goto bail;
@@ -1711,6 +1984,7 @@ ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
                     sp<AaptAssets> nextOverlay = new AaptAssets();
                     current->setOverlay(nextOverlay);
                     current = nextOverlay;
+                    current->setFullResPaths(mFullResPaths);
                 }
                 count = current->slurpResourceTree(bundle, String8(res));
 
@@ -1753,7 +2027,7 @@ ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
          * guarantees about ordering, so we're okay with an inorder search
          * using whatever order the OS happens to hand back to us.
          */
-        count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8());
+        count = slurpFullTree(bundle, assetRoot, AaptGroupEntry(), String8(), mFullAssetPaths);
         if (count < 0) {
             /* failure; report error and remove archive */
             totalCount = count;
@@ -1772,6 +2046,11 @@ ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
         goto bail;
     }
 
+    count = filter(bundle);
+    if (count != NO_ERROR) {
+        totalCount = count;
+        goto bail;
+    }
 
 bail:
     return totalCount;
@@ -1779,9 +2058,10 @@ bail:
 
 ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
                                     const AaptGroupEntry& kind,
-                                    const String8& resType)
+                                    const String8& resType,
+                                    sp<FilePathStore>& fullResPaths)
 {
-    ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType);
+    ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths);
     if (res > 0) {
         mGroupEntries.add(kind);
     }
@@ -1828,9 +2108,9 @@ ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
             continue;
         }
 
-        if (bundle->getMaxResVersion() != NULL && group.version.length() != 0) {
+        if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
             int maxResInt = atoi(bundle->getMaxResVersion());
-            const char *verString = group.version.string();
+            const char *verString = group.getVersionString().string();
             int dirVersionInt = atoi(verString + 1); // skip 'v' in version name
             if (dirVersionInt > maxResInt) {
               fprintf(stderr, "max res %d, skipping %s\n", maxResInt, entry->d_name);
@@ -1841,9 +2121,9 @@ ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
         FileType type = getFileType(subdirName.string());
 
         if (type == kFileTypeDirectory) {
-            sp<AaptDir> dir = makeDir(String8(entry->d_name));
+            sp<AaptDir> dir = makeDir(resType);
             ssize_t res = dir->slurpFullTree(bundle, subdirName, group,
-                                                resType);
+                                                resType, mFullResPaths);
             if (res < 0) {
                 count = res;
                 goto bail;
@@ -1853,7 +2133,13 @@ ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
                 count += res;
             }
 
-            mDirs.add(dir);
+            // Only add this directory if we don't already have a resource dir
+            // for the current type.  This ensures that we only add the dir once
+            // for all configs.
+            sp<AaptDir> rdir = resDir(resType);
+            if (rdir == NULL) {
+                mResDirs.add(dir);
+            }
         } else {
             if (bundle->getVerbose()) {
                 fprintf(stderr, "   (ignoring file '%s')\n", subdirName.string());
@@ -1962,6 +2248,142 @@ bail:
     return count;
 }
 
+status_t AaptAssets::filter(Bundle* bundle)
+{
+    ResourceFilter reqFilter;
+    status_t err = reqFilter.parse(bundle->getConfigurations());
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    ResourceFilter prefFilter;
+    err = prefFilter.parse(bundle->getPreferredConfigurations());
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    if (reqFilter.isEmpty() && prefFilter.isEmpty()) {
+        return NO_ERROR;
+    }
+
+    if (bundle->getVerbose()) {
+        if (!reqFilter.isEmpty()) {
+            printf("Applying required filter: %s\n",
+                    bundle->getConfigurations());
+        }
+        if (!prefFilter.isEmpty()) {
+            printf("Applying preferred filter: %s\n",
+                    bundle->getPreferredConfigurations());
+        }
+    }
+
+    const Vector<sp<AaptDir> >& resdirs = mResDirs;
+    const size_t ND = resdirs.size();
+    for (size_t i=0; i<ND; i++) {
+        const sp<AaptDir>& dir = resdirs.itemAt(i);
+        if (dir->getLeaf() == kValuesDir) {
+            // The "value" dir is special since a single file defines
+            // multiple resources, so we can not do filtering on the
+            // files themselves.
+            continue;
+        }
+        if (dir->getLeaf() == kMipmapDir) {
+            // We also skip the "mipmap" directory, since the point of this
+            // is to include all densities without stripping.  If you put
+            // other configurations in here as well they won't be stripped
+            // either...  So don't do that.  Seriously.  What is wrong with you?
+            continue;
+        }
+
+        const size_t NG = dir->getFiles().size();
+        for (size_t j=0; j<NG; j++) {
+            sp<AaptGroup> grp = dir->getFiles().valueAt(j);
+
+            // First remove any configurations we know we don't need.
+            for (size_t k=0; k<grp->getFiles().size(); k++) {
+                sp<AaptFile> file = grp->getFiles().valueAt(k);
+                if (k == 0 && grp->getFiles().size() == 1) {
+                    // If this is the only file left, we need to keep it.
+                    // Otherwise the resource IDs we are using will be inconsistent
+                    // with what we get when not stripping.  Sucky, but at least
+                    // for now we can rely on the back-end doing another filtering
+                    // pass to take this out and leave us with this resource name
+                    // containing no entries.
+                    continue;
+                }
+                if (file->getPath().getPathExtension() == ".xml") {
+                    // We can't remove .xml files at this point, because when
+                    // we parse them they may add identifier resources, so
+                    // removing them can cause our resource identifiers to
+                    // become inconsistent.
+                    continue;
+                }
+                const ResTable_config& config(file->getGroupEntry().toParams());
+                if (!reqFilter.match(config)) {
+                    if (bundle->getVerbose()) {
+                        printf("Pruning unneeded resource: %s\n",
+                                file->getPrintableSource().string());
+                    }
+                    grp->removeFile(k);
+                    k--;
+                }
+            }
+
+            // Quick check: no preferred filters, nothing more to do.
+            if (prefFilter.isEmpty()) {
+                continue;
+            }
+
+            // Now deal with preferred configurations.
+            for (int axis=AXIS_START; axis<=AXIS_END; axis++) {
+                for (size_t k=0; k<grp->getFiles().size(); k++) {
+                    sp<AaptFile> file = grp->getFiles().valueAt(k);
+                    if (k == 0 && grp->getFiles().size() == 1) {
+                        // If this is the only file left, we need to keep it.
+                        // Otherwise the resource IDs we are using will be inconsistent
+                        // with what we get when not stripping.  Sucky, but at least
+                        // for now we can rely on the back-end doing another filtering
+                        // pass to take this out and leave us with this resource name
+                        // containing no entries.
+                        continue;
+                    }
+                    if (file->getPath().getPathExtension() == ".xml") {
+                        // We can't remove .xml files at this point, because when
+                        // we parse them they may add identifier resources, so
+                        // removing them can cause our resource identifiers to
+                        // become inconsistent.
+                        continue;
+                    }
+                    const ResTable_config& config(file->getGroupEntry().toParams());
+                    if (!prefFilter.match(axis, config)) {
+                        // This is a resource we would prefer not to have.  Check
+                        // to see if have a similar variation that we would like
+                        // to have and, if so, we can drop it.
+                        for (size_t m=0; m<grp->getFiles().size(); m++) {
+                            if (m == k) continue;
+                            sp<AaptFile> mfile = grp->getFiles().valueAt(m);
+                            const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
+                            if (AaptGroupEntry::configSameExcept(config, mconfig, axis)) {
+                                if (prefFilter.match(axis, mconfig)) {
+                                    if (bundle->getVerbose()) {
+                                        printf("Pruning unneeded resource: %s\n",
+                                                file->getPrintableSource().string());
+                                    }
+                                    grp->removeFile(k);
+                                    k--;
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    return NO_ERROR;
+}
+
 sp<AaptSymbols> AaptAssets::getSymbolsFor(const String8& name)
 {
     sp<AaptSymbols> sym = mSymbols.valueFor(name);
@@ -2005,26 +2427,39 @@ const ResTable& AaptAssets::getIncludedResources() const
     return mIncludedAssets.getResources(false);
 }
 
-void AaptAssets::print() const
+void AaptAssets::print(const String8& prefix) const
 {
-    printf("Locale/Vendor pairs:\n");
+    String8 innerPrefix(prefix);
+    innerPrefix.append("  ");
+    String8 innerInnerPrefix(innerPrefix);
+    innerInnerPrefix.append("  ");
+    printf("%sConfigurations:\n", prefix.string());
     const size_t N=mGroupEntries.size();
     for (size_t i=0; i<N; i++) {
-        printf("   %s/%s\n",
-               mGroupEntries.itemAt(i).locale.string(),
-               mGroupEntries.itemAt(i).vendor.string());
+        String8 cname = mGroupEntries.itemAt(i).toDirName(String8());
+        printf("%s %s\n", prefix.string(),
+                cname != "" ? cname.string() : "(default)");
     }
 
-    printf("\nFiles:\n");
-    AaptDir::print();
+    printf("\n%sFiles:\n", prefix.string());
+    AaptDir::print(innerPrefix);
+
+    printf("\n%sResource Dirs:\n", prefix.string());
+    const Vector<sp<AaptDir> >& resdirs = mResDirs;
+    const size_t NR = resdirs.size();
+    for (size_t i=0; i<NR; i++) {
+        const sp<AaptDir>& d = resdirs.itemAt(i);
+        printf("%s  Type %s\n", prefix.string(), d->getLeaf().string());
+        d->print(innerInnerPrefix);
+    }
 }
 
-sp<AaptDir> AaptAssets::resDir(const String8& name)
+sp<AaptDir> AaptAssets::resDir(const String8& name) const
 {
-    const Vector<sp<AaptDir> >& dirs = mDirs;
-    const size_t N = dirs.size();
+    const Vector<sp<AaptDir> >& resdirs = mResDirs;
+    const size_t N = resdirs.size();
     for (size_t i=0; i<N; i++) {
-        const sp<AaptDir>& d = dirs.itemAt(i);
+        const sp<AaptDir>& d = resdirs.itemAt(i);
         if (d->getLeaf() == name) {
             return d;
         }
index eeb00c0be1ba0fe813b5cb0bcba3eecbfb82aeb2..d5345b277caff2b90150bfbb7a9c30399a5d02d4 100644 (file)
@@ -24,6 +24,8 @@ using namespace android;
 
 bool valid_symbol_name(const String8& str);
 
+class AaptAssets;
+
 enum {
     AXIS_NONE = 0,
     AXIS_MCC = 1,
@@ -42,7 +44,13 @@ enum {
     AXIS_NAVHIDDEN,
     AXIS_NAVIGATION,
     AXIS_SCREENSIZE,
-    AXIS_VERSION
+    AXIS_SMALLESTSCREENWIDTHDP,
+    AXIS_SCREENWIDTHDP,
+    AXIS_SCREENHEIGHTDP,
+    AXIS_VERSION,
+
+    AXIS_START = AXIS_MCC,
+    AXIS_END = AXIS_VERSION,
 };
 
 enum {
@@ -52,6 +60,8 @@ enum {
     SDK_ECLAIR_0_1 = 6,
     SDK_MR1 = 7,
     SDK_FROYO = 8,
+    SDK_HONEYCOMB_MR2 = 13,
+    SDK_ICE_CREAM_SANDWICH = 14,
 };
 
 /**
@@ -61,32 +71,19 @@ enum {
 struct AaptGroupEntry
 {
 public:
-    AaptGroupEntry() { }
+    AaptGroupEntry() : mParamsChanged(true) { }
     AaptGroupEntry(const String8& _locale, const String8& _vendor)
-        : locale(_locale), vendor(_vendor) { }
-
-    String8 mcc;
-    String8 mnc;
-    String8 locale;
-    String8 vendor;
-    String8 screenLayoutSize;
-    String8 screenLayoutLong;
-    String8 orientation;
-    String8 uiModeType;
-    String8 uiModeNight;
-    String8 density;
-    String8 touchscreen;
-    String8 keysHidden;
-    String8 keyboard;
-    String8 navHidden;
-    String8 navigation;
-    String8 screenSize;
-    String8 version;
+        : locale(_locale), vendor(_vendor), mParamsChanged(true) { }
 
     bool initFromDirName(const char* dir, String8* resType);
 
     static status_t parseNamePart(const String8& part, int* axis, uint32_t* value);
-    
+
+    static uint32_t getConfigValueForAxis(const ResTable_config& config, int axis);
+
+    static bool configSameExcept(const ResTable_config& config,
+            const ResTable_config& otherConfig, int axis);
+
     static bool getMccName(const char* name, ResTable_config* out = NULL);
     static bool getMncName(const char* name, ResTable_config* out = NULL);
     static bool getLocaleName(const char* name, ResTable_config* out = NULL);
@@ -102,11 +99,14 @@ public:
     static bool getNavigationName(const char* name, ResTable_config* out = NULL);
     static bool getNavHiddenName(const char* name, ResTable_config* out = NULL);
     static bool getScreenSizeName(const char* name, ResTable_config* out = NULL);
+    static bool getSmallestScreenWidthDpName(const char* name, ResTable_config* out = NULL);
+    static bool getScreenWidthDpName(const char* name, ResTable_config* out = NULL);
+    static bool getScreenHeightDpName(const char* name, ResTable_config* out = NULL);
     static bool getVersionName(const char* name, ResTable_config* out = NULL);
 
     int compare(const AaptGroupEntry& o) const;
 
-    ResTable_config toParams() const;
+    const ResTable_config& toParams() const;
 
     inline bool operator<(const AaptGroupEntry& o) const { return compare(o) < 0; }
     inline bool operator<=(const AaptGroupEntry& o) const { return compare(o) <= 0; }
@@ -117,6 +117,33 @@ public:
 
     String8 toString() const;
     String8 toDirName(const String8& resType) const;
+
+    const String8& getVersionString() const { return version; }
+
+private:
+    String8 mcc;
+    String8 mnc;
+    String8 locale;
+    String8 vendor;
+    String8 smallestScreenWidthDp;
+    String8 screenWidthDp;
+    String8 screenHeightDp;
+    String8 screenLayoutSize;
+    String8 screenLayoutLong;
+    String8 orientation;
+    String8 uiModeType;
+    String8 uiModeNight;
+    String8 density;
+    String8 touchscreen;
+    String8 keysHidden;
+    String8 keyboard;
+    String8 navHidden;
+    String8 navigation;
+    String8 screenSize;
+    String8 version;
+
+    mutable bool mParamsChanged;
+    mutable ResTable_config mParams;
 };
 
 inline int compare_type(const AaptGroupEntry& lhs, const AaptGroupEntry& rhs)
@@ -130,6 +157,7 @@ inline int strictly_order_type(const AaptGroupEntry& lhs, const AaptGroupEntry&
 }
 
 class AaptGroup;
+class FilePathStore;
 
 /**
  * A single asset file we know about.
@@ -214,7 +242,7 @@ public:
     status_t addFile(const sp<AaptFile>& file);
     void removeFile(size_t index);
 
-    void print() const;
+    void print(const String8& prefix) const;
 
     String8 getPrintableSource() const;
 
@@ -226,7 +254,7 @@ private:
 };
 
 /**
- * A single directory of assets, which can contain for files and other
+ * A single directory of assets, which can contain files and other
  * sub-directories.
  */
 class AaptDir : public RefBase
@@ -243,24 +271,11 @@ public:
     const DefaultKeyedVector<String8, sp<AaptGroup> >& getFiles() const { return mFiles; }
     const DefaultKeyedVector<String8, sp<AaptDir> >& getDirs() const { return mDirs; }
 
-    status_t addFile(const String8& name, const sp<AaptGroup>& file);
-    status_t addDir(const String8& name, const sp<AaptDir>& dir);
-
-    sp<AaptDir> makeDir(const String8& name);
+    virtual status_t addFile(const String8& name, const sp<AaptGroup>& file);
 
     void removeFile(const String8& name);
     void removeDir(const String8& name);
 
-    status_t renameFile(const sp<AaptFile>& file, const String8& newName);
-
-    status_t addLeafFile(const String8& leafName,
-                         const sp<AaptFile>& file);
-
-    virtual ssize_t slurpFullTree(Bundle* bundle,
-                                  const String8& srcDir,
-                                  const AaptGroupEntry& kind,
-                                  const String8& resType);
-
     /*
      * Perform some sanity checks on the names of files and directories here.
      * In particular:
@@ -280,11 +295,23 @@ public:
      */
     status_t validate() const;
 
-    void print() const;
+    void print(const String8& prefix) const;
 
     String8 getPrintableSource() const;
 
 private:
+    friend class AaptAssets;
+
+    status_t addDir(const String8& name, const sp<AaptDir>& dir);
+    sp<AaptDir> makeDir(const String8& name);
+    status_t addLeafFile(const String8& leafName,
+                         const sp<AaptFile>& file);
+    virtual ssize_t slurpFullTree(Bundle* bundle,
+                                  const String8& srcDir,
+                                  const AaptGroupEntry& kind,
+                                  const String8& resType,
+                                  sp<FilePathStore>& fullResPaths);
+
     String8 mLeaf;
     String8 mPath;
 
@@ -474,6 +501,14 @@ public:
     ResourceTypeSet();
 };
 
+// Storage for lists of fully qualified paths for
+// resources encountered during slurping.
+class FilePathStore : public RefBase,
+                      public Vector<String8>
+{
+public:
+    FilePathStore();
+};
 
 /**
  * Asset hierarchy being operated on.
@@ -481,13 +516,15 @@ public:
 class AaptAssets : public AaptDir
 {
 public:
-    AaptAssets() : AaptDir(String8(), String8()), mHaveIncludedAssets(false), mRes(NULL) { }
+    AaptAssets();
     virtual ~AaptAssets() { delete mRes; }
 
     const String8& getPackage() const { return mPackage; }
     void setPackage(const String8& package) { mPackage = package; mSymbolsPrivatePackage = package; }
 
-    const SortedVector<AaptGroupEntry>& getGroupEntries() const { return mGroupEntries; }
+    const SortedVector<AaptGroupEntry>& getGroupEntries() const;
+
+    virtual status_t addFile(const String8& name, const sp<AaptGroup>& file);
 
     sp<AaptFile> addFile(const String8& filePath,
                          const AaptGroupEntry& entry,
@@ -504,14 +541,6 @@ public:
     
     ssize_t slurpFromArgs(Bundle* bundle);
 
-    virtual ssize_t slurpFullTree(Bundle* bundle,
-                                  const String8& srcDir,
-                                  const AaptGroupEntry& kind,
-                                  const String8& resType);
-
-    ssize_t slurpResourceTree(Bundle* bundle, const String8& srcDir);
-    ssize_t slurpResourceZip(Bundle* bundle, const char* filename);
-
     sp<AaptSymbols> getSymbolsFor(const String8& name);
 
     const DefaultKeyedVector<String8, sp<AaptSymbols> >& getSymbols() const { return mSymbols; }
@@ -523,10 +552,10 @@ public:
     status_t addIncludedResources(const sp<AaptFile>& file);
     const ResTable& getIncludedResources() const;
 
-    void print() const;
+    void print(const String8& prefix) const;
 
-    inline const Vector<sp<AaptDir> >& resDirs() { return mDirs; }
-    sp<AaptDir> resDir(const String8& name);
+    inline const Vector<sp<AaptDir> >& resDirs() const { return mResDirs; }
+    sp<AaptDir> resDir(const String8& name) const;
 
     inline sp<AaptAssets> getOverlay() { return mOverlay; }
     inline void setOverlay(sp<AaptAssets>& overlay) { mOverlay = overlay; }
@@ -535,19 +564,43 @@ public:
     inline void 
         setResources(KeyedVector<String8, sp<ResourceTypeSet> >* res) { delete mRes; mRes = res; }
 
+    inline sp<FilePathStore>& getFullResPaths() { return mFullResPaths; }
+    inline void
+        setFullResPaths(sp<FilePathStore>& res) { mFullResPaths = res; }
+
+    inline sp<FilePathStore>& getFullAssetPaths() { return mFullAssetPaths; }
+    inline void
+        setFullAssetPaths(sp<FilePathStore>& res) { mFullAssetPaths = res; }
+
 private:
+    virtual ssize_t slurpFullTree(Bundle* bundle,
+                                  const String8& srcDir,
+                                  const AaptGroupEntry& kind,
+                                  const String8& resType,
+                                  sp<FilePathStore>& fullResPaths);
+
+    ssize_t slurpResourceTree(Bundle* bundle, const String8& srcDir);
+    ssize_t slurpResourceZip(Bundle* bundle, const char* filename);
+
+    status_t filter(Bundle* bundle);
+
     String8 mPackage;
     SortedVector<AaptGroupEntry> mGroupEntries;
     DefaultKeyedVector<String8, sp<AaptSymbols> > mSymbols;
     String8 mSymbolsPrivatePackage;
 
-    Vector<sp<AaptDir> > mDirs;
+    Vector<sp<AaptDir> > mResDirs;
+
+    bool mChanged;
 
     bool mHaveIncludedAssets;
     AssetManager mIncludedAssets;
 
     sp<AaptAssets> mOverlay;
     KeyedVector<String8, sp<ResourceTypeSet> >* mRes;
+
+    sp<FilePathStore> mFullResPaths;
+    sp<FilePathStore> mFullAssetPaths;
 };
 
 #endif // __AAPT_ASSETS_H
index 094b7db3b4c4c9cd7f9b46a165c4718b25fb87da..cb55a9cfd4d688ff9974460b0e4b61927f12964c 100644 (file)
@@ -13,10 +13,13 @@ include $(CLEAR_VARS)
 LOCAL_SRC_FILES := \
        AaptAssets.cpp \
        Command.cpp \
+       CrunchCache.cpp \
+       FileFinder.cpp \
        Main.cpp \
        Package.cpp \
        StringPool.cpp \
        XMLNode.cpp \
+       ResourceFilter.cpp \
        ResourceTable.cpp \
        Images.cpp \
        Resource.cpp \
@@ -41,7 +44,7 @@ LOCAL_STATIC_LIBRARIES := \
        libpng
 
 ifeq ($(HOST_OS),linux)
-LOCAL_LDLIBS += -lrt -lpthread
+LOCAL_LDLIBS += -lrt -ldl -lpthread
 endif
 
 # Statically link libz for MinGW (Win SDK under Linux),
index fa84e938cab4732749f3ca36634947ae8891de95..2d1060ba23a07cf826242140af9abbaa69dcba3a 100644 (file)
--- a/Bundle.h
+++ b/Bundle.h
@@ -25,6 +25,7 @@ typedef enum Command {
     kCommandAdd,
     kCommandRemove,
     kCommandPackage,
+    kCommandCrunch,
 } Command;
 
 /*
@@ -41,13 +42,15 @@ public:
           mCompressionMethod(0), mOutputAPKFile(NULL),
           mManifestPackageNameOverride(NULL), mInstrumentationPackageNameOverride(NULL),
           mIsOverlayPackage(false),
-          mAutoAddOverlay(false), mAssetSourceDir(NULL), mProguardFile(NULL),
+          mAutoAddOverlay(false), mGenDependencies(false),
+          mAssetSourceDir(NULL), 
+          mCrunchedOutputDir(NULL), mProguardFile(NULL),
           mAndroidManifestFile(NULL), mPublicOutputFile(NULL),
           mRClassDir(NULL), mResourceIntermediatesDir(NULL), mManifestMinSdkVersion(NULL),
           mMinSdkVersion(NULL), mTargetSdkVersion(NULL), mMaxSdkVersion(NULL),
-          mVersionCode(NULL), mVersionName(NULL), mCustomPackage(NULL),
+          mVersionCode(NULL), mVersionName(NULL), mCustomPackage(NULL), mExtraPackages(NULL),
           mMaxResVersion(NULL), mDebugMode(false), mNonConstantId(false), mProduct(NULL),
-          mArgc(0), mArgv(NULL)
+          mUseCrunchCache(false), mArgc(0), mArgv(NULL)
         {}
     ~Bundle(void) {}
 
@@ -97,12 +100,16 @@ public:
     void setIsOverlayPackage(bool val) { mIsOverlayPackage = val; }
     bool getAutoAddOverlay() { return mAutoAddOverlay; }
     void setAutoAddOverlay(bool val) { mAutoAddOverlay = val; }
+    bool getGenDependencies() { return mGenDependencies; }
+    void setGenDependencies(bool val) { mGenDependencies = val; }
 
     /*
      * Input options.
      */
     const char* getAssetSourceDir() const { return mAssetSourceDir; }
     void setAssetSourceDir(const char* dir) { mAssetSourceDir = dir; }
+    const char* getCrunchedOutputDir() const { return mCrunchedOutputDir; }
+    void setCrunchedOutputDir(const char* dir) { mCrunchedOutputDir = dir; }
     const char* getProguardFile() const { return mProguardFile; }
     void setProguardFile(const char* file) { mProguardFile = file; }
     const android::Vector<const char*>& getResourceSourceDirs() const { return mResourceSourceDirs; }
@@ -115,6 +122,8 @@ public:
     void setRClassDir(const char* dir) { mRClassDir = dir; }
     const char* getConfigurations() const { return mConfigurations.size() > 0 ? mConfigurations.string() : NULL; }
     void addConfigurations(const char* val) { if (mConfigurations.size() > 0) { mConfigurations.append(","); mConfigurations.append(val); } else { mConfigurations = val; } }
+    const char* getPreferredConfigurations() const { return mPreferredConfigurations.size() > 0 ? mPreferredConfigurations.string() : NULL; }
+    void addPreferredConfigurations(const char* val) { if (mPreferredConfigurations.size() > 0) { mPreferredConfigurations.append(","); mPreferredConfigurations.append(val); } else { mPreferredConfigurations = val; } }
     const char* getResourceIntermediatesDir() const { return mResourceIntermediatesDir; }
     void setResourceIntermediatesDir(const char* dir) { mResourceIntermediatesDir = dir; }
     const android::Vector<const char*>& getPackageIncludes() const { return mPackageIncludes; }
@@ -138,6 +147,8 @@ public:
     void setVersionName(const char* val) { mVersionName = val; }
     const char* getCustomPackage() const { return mCustomPackage; }
     void setCustomPackage(const char* val) { mCustomPackage = val; }
+    const char* getExtraPackages() const { return mExtraPackages; }
+    void setExtraPackages(const char* val) { mExtraPackages = val; }
     const char* getMaxResVersion() const { return mMaxResVersion; }
     void setMaxResVersion(const char * val) { mMaxResVersion = val; }
     bool getDebugMode() { return mDebugMode; }
@@ -146,6 +157,8 @@ public:
     void setNonConstantId(bool val) { mNonConstantId = val; }
     const char* getProduct() const { return mProduct; }
     void setProduct(const char * val) { mProduct = val; }
+    void setUseCrunchCache(bool val) { mUseCrunchCache = val; }
+    bool getUseCrunchCache() { return mUseCrunchCache; }
 
     /*
      * Set and get the file specification.
@@ -224,13 +237,16 @@ private:
     const char* mInstrumentationPackageNameOverride;
     bool        mIsOverlayPackage;
     bool        mAutoAddOverlay;
+    bool        mGenDependencies;
     const char* mAssetSourceDir;
+    const char* mCrunchedOutputDir;
     const char* mProguardFile;
     const char* mAndroidManifestFile;
     const char* mPublicOutputFile;
     const char* mRClassDir;
     const char* mResourceIntermediatesDir;
     android::String8 mConfigurations;
+    android::String8 mPreferredConfigurations;
     android::Vector<const char*> mPackageIncludes;
     android::Vector<const char*> mJarFiles;
     android::Vector<const char*> mNoCompressExtensions;
@@ -243,10 +259,12 @@ private:
     const char* mVersionCode;
     const char* mVersionName;
     const char* mCustomPackage;
+    const char* mExtraPackages;
     const char* mMaxResVersion;
     bool        mDebugMode;
     bool        mNonConstantId;
     const char* mProduct;
+    bool        mUseCrunchCache;
 
     /* file specification */
     int         mArgc;
diff --git a/CacheUpdater.h b/CacheUpdater.h
new file mode 100644 (file)
index 0000000..0e65589
--- /dev/null
@@ -0,0 +1,107 @@
+//
+// Copyright 2011 The Android Open Source Project
+//
+// Abstraction of calls to system to make directories and delete files and
+// wrapper to image processing.
+
+#ifndef CACHE_UPDATER_H
+#define CACHE_UPDATER_H
+
+#include <utils/String8.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include "Images.h"
+
+using namespace android;
+
+/** CacheUpdater
+ *  This is a pure virtual class that declares abstractions of functions useful
+ *  for managing a cache files. This manager is set up to be used in a
+ *  mirror cache where the source tree is duplicated and filled with processed
+ *  images. This class is abstracted to allow for dependency injection during
+ *  unit testing.
+ *  Usage:
+ *      To update/add a file to the cache, call processImage
+ *      To remove a file from the cache, call deleteFile
+ */
+class CacheUpdater {
+public:
+    // Make sure all the directories along this path exist
+    virtual void ensureDirectoriesExist(String8 path) = 0;
+
+    // Delete a file
+    virtual void deleteFile(String8 path) = 0;
+
+    // Process an image from source out to dest
+    virtual void processImage(String8 source, String8 dest) = 0;
+private:
+};
+
+/** SystemCacheUpdater
+ * This is an implementation of the above virtual cache updater specification.
+ * This implementations hits the filesystem to manage a cache and calls out to
+ * the PNG crunching in images.h to process images out to its cache components.
+ */
+class SystemCacheUpdater : public CacheUpdater {
+public:
+    // Constructor to set bundle to pass to preProcessImage
+    SystemCacheUpdater (Bundle* b)
+        : bundle(b) { };
+
+    // Make sure all the directories along this path exist
+    virtual void ensureDirectoriesExist(String8 path)
+    {
+        // Check to see if we're dealing with a fully qualified path
+        String8 existsPath;
+        String8 toCreate;
+        String8 remains;
+        struct stat s;
+
+        // Check optomistically to see if all directories exist.
+        // If something in the path doesn't exist, then walk the path backwards
+        // and find the place to start creating directories forward.
+        if (stat(path.string(),&s) == -1) {
+            // Walk backwards to find place to start creating directories
+            existsPath = path;
+            do {
+                // As we remove the end of existsPath add it to
+                // the string of paths to create.
+                toCreate = existsPath.getPathLeaf().appendPath(toCreate);
+                existsPath = existsPath.getPathDir();
+            } while (stat(existsPath.string(),&s) == -1);
+
+            // Walk forwards and build directories as we go
+            do {
+                // Advance to the next segment of the path
+                existsPath.appendPath(toCreate.walkPath(&remains));
+                toCreate = remains;
+#ifdef HAVE_MS_C_RUNTIME
+                _mkdir(existsPath.string());
+#else
+                mkdir(existsPath.string(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP);
+#endif
+            } while (remains.length() > 0);
+        } //if
+    };
+
+    // Delete a file
+    virtual void deleteFile(String8 path)
+    {
+        if (remove(path.string()) != 0)
+            fprintf(stderr,"ERROR DELETING %s\n",path.string());
+    };
+
+    // Process an image from source out to dest
+    virtual void processImage(String8 source, String8 dest)
+    {
+        // Make sure we're trying to write to a directory that is extant
+        ensureDirectoriesExist(dest.getPathDir());
+
+        preProcessImageToCache(bundle, source, dest);
+    };
+private:
+    Bundle* bundle;
+};
+
+#endif // CACHE_UPDATER_H
\ No newline at end of file
index 739763edca34ed831a4d7815f1f25979a4b9a951..637c27da65f931891303b4cdeccc213d53b327ae 100644 (file)
@@ -5,6 +5,7 @@
 //
 #include "Main.h"
 #include "Bundle.h"
+#include "ResourceFilter.h"
 #include "ResourceTable.h"
 #include "XMLNode.h"
 
@@ -198,8 +199,10 @@ int doList(Bundle* bundle)
         if (&res == NULL) {
             printf("\nNo resource table found.\n");
         } else {
+#ifndef HAVE_ANDROID_OS
             printf("\nResource table:\n");
             res.print(false);
+#endif
         }
 
         Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml",
@@ -289,6 +292,27 @@ static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes,
     return value.data;
 }
 
+static int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXMLTree& tree,
+        uint32_t attrRes, String8* outError, int32_t defValue = -1)
+{
+    ssize_t idx = indexOfAttribute(tree, attrRes);
+    if (idx < 0) {
+        return defValue;
+    }
+    Res_value value;
+    if (tree.getAttributeValue(idx, &value) != NO_ERROR) {
+        if (value.dataType == Res_value::TYPE_REFERENCE) {
+            resTable->resolveReference(&value, 0);
+        }
+        if (value.dataType < Res_value::TYPE_FIRST_INT
+                || value.dataType > Res_value::TYPE_LAST_INT) {
+            if (outError != NULL) *outError = "attribute is not an integer value";
+            return defValue;
+        }
+    }
+    return value.data;
+}
+
 static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree,
         uint32_t attrRes, String8* outError)
 {
@@ -318,11 +342,12 @@ static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree&
 // These are attribute resource constants for the platform, as found
 // in android.R.attr
 enum {
+    LABEL_ATTR = 0x01010001,
+    ICON_ATTR = 0x01010002,
     NAME_ATTR = 0x01010003,
     VERSION_CODE_ATTR = 0x0101021b,
     VERSION_NAME_ATTR = 0x0101021c,
-    LABEL_ATTR = 0x01010001,
-    ICON_ATTR = 0x01010002,
+    SCREEN_ORIENTATION_ATTR = 0x0101001e,
     MIN_SDK_VERSION_ATTR = 0x0101020c,
     MAX_SDK_VERSION_ATTR = 0x01010271,
     REQ_TOUCH_SCREEN_ATTR = 0x01010227,
@@ -341,6 +366,10 @@ enum {
     REQUIRED_ATTR = 0x0101028e,
     SCREEN_SIZE_ATTR = 0x010102ca,
     SCREEN_DENSITY_ATTR = 0x010102cb,
+    REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364,
+    COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365,
+    LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366,
+    PUBLIC_KEY_ATTR = 0x010103a6,
 };
 
 const char *getComponentName(String8 &pkgName, String8 &componentName) {
@@ -421,6 +450,24 @@ int doDump(Bundle* bundle)
         return 1;
     }
 
+    // Make a dummy config for retrieving resources...  we need to supply
+    // non-default values for some configs so that we can retrieve resources
+    // in the app that don't have a default.  The most important of these is
+    // the API version because key resources like icons will have an implicit
+    // version if they are using newer config types like density.
+    ResTable_config config;
+    config.language[0] = 'e';
+    config.language[1] = 'n';
+    config.country[0] = 'U';
+    config.country[1] = 'S';
+    config.orientation = ResTable_config::ORIENTATION_PORT;
+    config.density = ResTable_config::DENSITY_MEDIUM;
+    config.sdkVersion = 10000; // Very high.
+    config.screenWidthDp = 320;
+    config.screenHeightDp = 480;
+    config.smallestScreenWidthDp = 320;
+    assets.setConfiguration(config);
+
     const ResTable& res = assets.getResources(false);
     if (&res == NULL) {
         fprintf(stderr, "ERROR: dump failed because no resource table was found\n");
@@ -428,8 +475,9 @@ int doDump(Bundle* bundle)
     }
 
     if (strcmp("resources", option) == 0) {
+#ifndef HAVE_ANDROID_OS
         res.print(bundle->getValues());
-
+#endif
     } else if (strcmp("xmltree", option) == 0) {
         if (bundle->getFileSpecCount() < 3) {
             fprintf(stderr, "ERROR: no dump xmltree resource file specified\n");
@@ -539,6 +587,19 @@ int doDump(Bundle* bundle)
                 }
             }
         } else if (strcmp("badging", option) == 0) {
+            Vector<String8> locales;
+            res.getLocales(&locales);
+
+            Vector<ResTable_config> configs;
+            res.getConfigurations(&configs);
+            SortedVector<int> densities;
+            const size_t NC = configs.size();
+            for (size_t i=0; i<NC; i++) {
+                int dens = configs[i].density;
+                if (dens == 0) dens = 160;
+                densities.add(dens);
+            }
+
             size_t len;
             ResXMLTree::event_code_t code;
             int depth = 0;
@@ -595,6 +656,10 @@ int doDump(Bundle* bundle)
             bool specTouchscreenFeature = false; // touchscreen-related
             bool specMultitouchFeature = false;
             bool reqDistinctMultitouchFeature = false;
+            bool specScreenPortraitFeature = false;
+            bool specScreenLandscapeFeature = false;
+            bool reqScreenPortraitFeature = false;
+            bool reqScreenLandscapeFeature = false;
             // 2.2 also added some other features that apps can request, but that
             // have no corresponding permission, so we cannot implement any
             // back-compatibility heuristic for them. The below are thus unnecessary
@@ -611,6 +676,9 @@ int doDump(Bundle* bundle)
             int largeScreen = 1;
             int xlargeScreen = 1;
             int anyDensity = 1;
+            int requiresSmallestWidthDp = 0;
+            int compatibleWidthLimitDp = 0;
+            int largestWidthLimitDp = 0;
             String8 pkg;
             String8 activityName;
             String8 activityLabel;
@@ -625,10 +693,11 @@ int doDump(Bundle* bundle)
                     } else if (depth < 3) {
                         if (withinActivity && isMainActivity && isLauncherActivity) {
                             const char *aName = getComponentName(pkg, activityName);
+                            printf("launchable-activity:");
                             if (aName != NULL) {
-                                printf("launchable activity name='%s'", aName);
+                                printf(" name='%s' ", aName);
                             }
-                            printf("label='%s' icon='%s'\n",
+                            printf(" label='%s' icon='%s'\n",
                                     activityLabel.string(),
                                     activityIcon.string());
                         }
@@ -693,23 +762,51 @@ int doDump(Bundle* bundle)
                     withinApplication = false;
                     if (tag == "application") {
                         withinApplication = true;
-                        String8 label = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
-                         if (error != "") {
-                             fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string());
-                             goto bail;
+
+                        String8 label;
+                        const size_t NL = locales.size();
+                        for (size_t i=0; i<NL; i++) {
+                            const char* localeStr =  locales[i].string();
+                            assets.setLocale(localeStr != NULL ? localeStr : "");
+                            String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error);
+                            if (llabel != "") {
+                                if (localeStr == NULL || strlen(localeStr) == 0) {
+                                    label = llabel;
+                                    printf("application-label:'%s'\n", llabel.string());
+                                } else {
+                                    if (label == "") {
+                                        label = llabel;
+                                    }
+                                    printf("application-label-%s:'%s'\n", localeStr,
+                                            llabel.string());
+                                }
+                            }
                         }
-                        printf("application: label='%s' ", label.string());
+
+                        ResTable_config tmpConfig = config;
+                        const size_t ND = densities.size();
+                        for (size_t i=0; i<ND; i++) {
+                            tmpConfig.density = densities[i];
+                            assets.setConfiguration(tmpConfig);
+                            String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
+                            if (icon != "") {
+                                printf("application-icon-%d:'%s'\n", densities[i], icon.string());
+                            }
+                        }
+                        assets.setConfiguration(config);
+
                         String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error);
                         if (error != "") {
                             fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
                             goto bail;
                         }
-                        printf("icon='%s'\n", icon.string());
                         int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0);
                         if (error != "") {
                             fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string());
                             goto bail;
                         }
+                        printf("application: label='%s' ", label.string());
+                        printf("icon='%s'\n", icon.string());
                         if (testOnly != 0) {
                             printf("testOnly='%d'\n", testOnly);
                         }
@@ -789,6 +886,12 @@ int doDump(Bundle* bundle)
                                 XLARGE_SCREEN_ATTR, NULL, 1);
                         anyDensity = getIntegerAttribute(tree,
                                 ANY_DENSITY_ATTR, NULL, 1);
+                        requiresSmallestWidthDp = getIntegerAttribute(tree,
+                                REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0);
+                        compatibleWidthLimitDp = getIntegerAttribute(tree,
+                                COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0);
+                        largestWidthLimitDp = getIntegerAttribute(tree,
+                                LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0);
                     } else if (tag == "uses-feature") {
                         String8 name = getAttribute(tree, NAME_ATTR, &error);
 
@@ -834,6 +937,10 @@ int doDump(Bundle* bundle)
                                 // these have no corresponding permission to check for,
                                 // but should imply the foundational telephony permission
                                 reqTelephonySubFeature = true;
+                            } else if (name == "android.hardware.screen.portrait") {
+                                specScreenPortraitFeature = true;
+                            } else if (name == "android.hardware.screen.landscape") {
+                                specScreenLandscapeFeature = true;
                             }
                             printf("uses-feature%s:'%s'\n",
                                     req ? "" : "-not-required", name.string());
@@ -916,6 +1023,15 @@ int doDump(Bundle* bundle)
                     } else if (tag == "compatible-screens") {
                         printCompatibleScreens(tree);
                         depth--;
+                    } else if (tag == "package-verifier") {
+                        String8 name = getAttribute(tree, NAME_ATTR, &error);
+                        if (name != "" && error == "") {
+                            String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error);
+                            if (publicKey != "" && error == "") {
+                                printf("package-verifier: name='%s' publicKey='%s'\n",
+                                        name.string(), publicKey.string());
+                            }
+                        }
                     }
                 } else if (depth == 3 && withinApplication) {
                     withinActivity = false;
@@ -941,6 +1057,18 @@ int doDump(Bundle* bundle)
                             fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string());
                             goto bail;
                         }
+
+                        int32_t orien = getResolvedIntegerAttribute(&res, tree,
+                                SCREEN_ORIENTATION_ATTR, &error);
+                        if (error == "") {
+                            if (orien == 0 || orien == 6 || orien == 8) {
+                                // Requests landscape, sensorLandscape, or reverseLandscape.
+                                reqScreenLandscapeFeature = true;
+                            } else if (orien == 1 || orien == 7 || orien == 9) {
+                                // Requests portrait, sensorPortrait, or reversePortrait.
+                                reqScreenPortraitFeature = true;
+                            }
+                        }
                     } else if (tag == "uses-library") {
                         String8 libraryName = getAttribute(tree, NAME_ATTR, &error);
                         if (error != "") {
@@ -1100,6 +1228,19 @@ int doDump(Bundle* bundle)
                 printf("uses-feature:'android.hardware.touchscreen.multitouch'\n");
             }
 
+            // Landscape/portrait-related compatibility logic
+            if (!specScreenLandscapeFeature && !specScreenPortraitFeature) {
+                // If the app has specified any activities in its manifest
+                // that request a specific orientation, then assume that
+                // orientation is required.
+                if (reqScreenLandscapeFeature) {
+                    printf("uses-feature:'android.hardware.screen.landscape'\n");
+                }
+                if (reqScreenPortraitFeature) {
+                    printf("uses-feature:'android.hardware.screen.portrait'\n");
+                }
+            }
+
             if (hasMainActivity) {
                 printf("main\n");
             }
@@ -1125,6 +1266,34 @@ int doDump(Bundle* bundle)
                 printf("other-services\n");
             }
 
+            // For modern apps, if screen size buckets haven't been specified
+            // but the new width ranges have, then infer the buckets from them.
+            if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0
+                    && requiresSmallestWidthDp > 0) {
+                int compatWidth = compatibleWidthLimitDp;
+                if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp;
+                if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) {
+                    smallScreen = -1;
+                } else {
+                    smallScreen = 0;
+                }
+                if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) {
+                    normalScreen = -1;
+                } else {
+                    normalScreen = 0;
+                }
+                if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) {
+                    largeScreen = -1;
+                } else {
+                    largeScreen = 0;
+                }
+                if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) {
+                    xlargeScreen = -1;
+                } else {
+                    xlargeScreen = 0;
+                }
+            }
+
             // Determine default values for any unspecified screen sizes,
             // based on the target SDK of the package.  As of 4 (donut)
             // the screen size support was introduced, so all default to
@@ -1143,7 +1312,8 @@ int doDump(Bundle* bundle)
                 xlargeScreen = targetSdk >= 9 ? -1 : 0;
             }
             if (anyDensity > 0) {
-                anyDensity = targetSdk >= 4 ? -1 : 0;
+                anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0
+                        || compatibleWidthLimitDp > 0) ? -1 : 0;
             }
             printf("supports-screens:");
             if (smallScreen != 0) printf(" 'small'");
@@ -1151,12 +1321,18 @@ int doDump(Bundle* bundle)
             if (largeScreen != 0) printf(" 'large'");
             if (xlargeScreen != 0) printf(" 'xlarge'");
             printf("\n");
-
             printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false");
+            if (requiresSmallestWidthDp > 0) {
+                printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp);
+            }
+            if (compatibleWidthLimitDp > 0) {
+                printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp);
+            }
+            if (largestWidthLimitDp > 0) {
+                printf("largest-width-limit:'%d'\n", largestWidthLimitDp);
+            }
 
             printf("locales:");
-            Vector<String8> locales;
-            res.getLocales(&locales);
             const size_t NL = locales.size();
             for (size_t i=0; i<NL; i++) {
                 const char* localeStr =  locales[i].string();
@@ -1167,16 +1343,6 @@ int doDump(Bundle* bundle)
             }
             printf("\n");
 
-            Vector<ResTable_config> configs;
-            res.getConfigurations(&configs);
-            SortedVector<int> densities;
-            const size_t NC = configs.size();
-            for (size_t i=0; i<NC; i++) {
-                int dens = configs[i].density;
-                if (dens == 0) dens = 160;
-                densities.add(dens);
-            }
-
             printf("densities:");
             const size_t ND = densities.size();
             for (size_t i=0; i<ND; i++) {
@@ -1353,6 +1519,8 @@ int doPackage(Bundle* bundle)
     status_t err;
     sp<AaptAssets> assets;
     int N;
+    FILE* fp;
+    String8 dependencyFile;
 
     // -c zz_ZZ means do pseudolocalization
     ResourceFilter filter;
@@ -1387,16 +1555,28 @@ int doPackage(Bundle* bundle)
 
     // Load the assets.
     assets = new AaptAssets();
+
+    // Set up the resource gathering in assets if we're going to generate
+    // dependency files. Every time we encounter a resource while slurping
+    // the tree, we'll add it to these stores so we have full resource paths
+    // to write to a dependency file.
+    if (bundle->getGenDependencies()) {
+        sp<FilePathStore> resPathStore = new FilePathStore;
+        assets->setFullResPaths(resPathStore);
+        sp<FilePathStore> assetPathStore = new FilePathStore;
+        assets->setFullAssetPaths(assetPathStore);
+    }
+
     err = assets->slurpFromArgs(bundle);
     if (err < 0) {
         goto bail;
     }
 
     if (bundle->getVerbose()) {
-        assets->print();
+        assets->print(String8());
     }
 
-    // If they asked for any files that need to be compiled, do so.
+    // If they asked for any fileAs that need to be compiled, do so.
     if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
         err = buildResources(bundle, assets);
         if (err != 0) {
@@ -1410,10 +1590,46 @@ int doPackage(Bundle* bundle)
         goto bail;
     }
 
+    // If we've been asked to generate a dependency file, do that here
+    if (bundle->getGenDependencies()) {
+        // If this is the packaging step, generate the dependency file next to
+        // the output apk (e.g. bin/resources.ap_.d)
+        if (outputAPKFile) {
+            dependencyFile = String8(outputAPKFile);
+            // Add the .d extension to the dependency file.
+            dependencyFile.append(".d");
+        } else {
+            // Else if this is the R.java dependency generation step,
+            // generate the dependency file in the R.java package subdirectory
+            // e.g. gen/com/foo/app/R.java.d
+            dependencyFile = String8(bundle->getRClassDir());
+            dependencyFile.appendPath("R.java.d");
+        }
+        // Make sure we have a clean dependency file to start with
+        fp = fopen(dependencyFile, "w");
+        fclose(fp);
+    }
+
     // Write out R.java constants
     if (assets->getPackage() == assets->getSymbolsPrivatePackage()) {
         if (bundle->getCustomPackage() == NULL) {
+            // Write the R.java file into the appropriate class directory
+            // e.g. gen/com/foo/app/R.java
             err = writeResourceSymbols(bundle, assets, assets->getPackage(), true);
+            // If we have library files, we're going to write our R.java file into
+            // the appropriate class directory for those libraries as well.
+            // e.g. gen/com/foo/app/lib/R.java
+            if (bundle->getExtraPackages() != NULL) {
+                // Split on colon
+                String8 libs(bundle->getExtraPackages());
+                char* packageString = strtok(libs.lockBuffer(libs.length()), ":");
+                while (packageString != NULL) {
+                    // Write the R.java file out with the correct package name
+                    err = writeResourceSymbols(bundle, assets, String8(packageString), true);
+                    packageString = strtok(NULL, ":");
+                }
+                libs.unlockBuffer();
+            }
         } else {
             const String8 customPkg(bundle->getCustomPackage());
             err = writeResourceSymbols(bundle, assets, customPkg, true);
@@ -1447,6 +1663,23 @@ int doPackage(Bundle* bundle)
         }
     }
 
+    // If we've been asked to generate a dependency file, we need to finish up here.
+    // the writeResourceSymbols and writeAPK functions have already written the target
+    // half of the dependency file, now we need to write the prerequisites. (files that
+    // the R.java file or .ap_ file depend on)
+    if (bundle->getGenDependencies()) {
+        // Now that writeResourceSymbols or writeAPK has taken care of writing
+        // the targets to our dependency file, we'll write the prereqs
+        fp = fopen(dependencyFile, "a+");
+        fprintf(fp, " : ");
+        bool includeRaw = (outputAPKFile != NULL);
+        err = writeDependencyPreReqs(bundle, assets, fp, includeRaw);
+        // Also manually add the AndroidManifeset since it's not under res/ or assets/
+        // and therefore was not added to our pathstores during slurping
+        fprintf(fp, "%s \\\n", bundle->getAndroidManifestFile());
+        fclose(fp);
+    }
+
     retVal = 0;
 bail:
     if (SourcePos::hasErrors()) {
@@ -1454,3 +1687,25 @@ bail:
     }
     return retVal;
 }
+
+/*
+ * Do PNG Crunching
+ * PRECONDITIONS
+ *  -S flag points to a source directory containing drawable* folders
+ *  -C flag points to destination directory. The folder structure in the
+ *     source directory will be mirrored to the destination (cache) directory
+ *
+ * POSTCONDITIONS
+ *  Destination directory will be updated to match the PNG files in
+ *  the source directory. 
+ */
+int doCrunch(Bundle* bundle)
+{
+    fprintf(stdout, "Crunching PNG Files in ");
+    fprintf(stdout, "source dir: %s\n", bundle->getResourceSourceDirs()[0]);
+    fprintf(stdout, "To destination dir: %s\n", bundle->getCrunchedOutputDir());
+
+    updatePreProcessedCache(bundle);
+
+    return NO_ERROR;
+}
diff --git a/CrunchCache.cpp b/CrunchCache.cpp
new file mode 100644 (file)
index 0000000..c4cf6bc
--- /dev/null
@@ -0,0 +1,104 @@
+//
+// Copyright 2011 The Android Open Source Project
+//
+// Implementation file for CrunchCache
+// This file defines functions laid out and documented in
+// CrunchCache.h
+
+#include <utils/Vector.h>
+#include <utils/String8.h>
+
+#include "DirectoryWalker.h"
+#include "FileFinder.h"
+#include "CacheUpdater.h"
+#include "CrunchCache.h"
+
+using namespace android;
+
+CrunchCache::CrunchCache(String8 sourcePath, String8 destPath, FileFinder* ff)
+    : mSourcePath(sourcePath), mDestPath(destPath), mSourceFiles(0), mDestFiles(0), mFileFinder(ff)
+{
+    // We initialize the default value to return to 0 so if a file doesn't exist
+    // then all files are automatically "newer" than it.
+
+    // Set file extensions to look for. Right now just pngs.
+    mExtensions.push(String8(".png"));
+
+    // Load files into our data members
+    loadFiles();
+}
+
+size_t CrunchCache::crunch(CacheUpdater* cu, bool forceOverwrite)
+{
+    size_t numFilesUpdated = 0;
+
+    // Iterate through the source files and compare to cache.
+    // After processing a file, remove it from the source files and
+    // from the dest files.
+    // We're done when we're out of files in source.
+    String8 relativePath;
+    while (mSourceFiles.size() > 0) {
+        // Get the full path to the source file, then convert to a c-string
+        // and offset our beginning pointer to the length of the sourcePath
+        // This efficiently strips the source directory prefix from our path.
+        // Also, String8 doesn't have a substring method so this is what we've
+        // got to work with.
+        const char* rPathPtr = mSourceFiles.keyAt(0).string()+mSourcePath.length();
+        // Strip leading slash if present
+        int offset = 0;
+        if (rPathPtr[0] == OS_PATH_SEPARATOR)
+            offset = 1;
+        relativePath = String8(rPathPtr + offset);
+
+        if (forceOverwrite || needsUpdating(relativePath)) {
+            cu->processImage(mSourcePath.appendPathCopy(relativePath),
+                             mDestPath.appendPathCopy(relativePath));
+            numFilesUpdated++;
+            // crunchFile(relativePath);
+        }
+        // Delete this file from the source files and (if it exists) from the
+        // dest files.
+        mSourceFiles.removeItemsAt(0);
+        mDestFiles.removeItem(mDestPath.appendPathCopy(relativePath));
+    }
+
+    // Iterate through what's left of destFiles and delete leftovers
+    while (mDestFiles.size() > 0) {
+        cu->deleteFile(mDestFiles.keyAt(0));
+        mDestFiles.removeItemsAt(0);
+    }
+
+    // Update our knowledge of the files cache
+    // both source and dest should be empty by now.
+    loadFiles();
+
+    return numFilesUpdated;
+}
+
+void CrunchCache::loadFiles()
+{
+    // Clear out our data structures to avoid putting in duplicates
+    mSourceFiles.clear();
+    mDestFiles.clear();
+
+    // Make a directory walker that points to the system.
+    DirectoryWalker* dw = new SystemDirectoryWalker();
+
+    // Load files in the source directory
+    mFileFinder->findFiles(mSourcePath, mExtensions, mSourceFiles,dw);
+
+    // Load files in the destination directory
+    mFileFinder->findFiles(mDestPath,mExtensions,mDestFiles,dw);
+
+    delete dw;
+}
+
+bool CrunchCache::needsUpdating(String8 relativePath) const
+{
+    // Retrieve modification dates for this file entry under the source and
+    // cache directory trees. The vectors will return a modification date of 0
+    // if the file doesn't exist.
+    time_t sourceDate = mSourceFiles.valueFor(mSourcePath.appendPathCopy(relativePath));
+    time_t destDate = mDestFiles.valueFor(mDestPath.appendPathCopy(relativePath));
+    return sourceDate > destDate;
+}
\ No newline at end of file
diff --git a/CrunchCache.h b/CrunchCache.h
new file mode 100644 (file)
index 0000000..be3da5c
--- /dev/null
@@ -0,0 +1,102 @@
+//
+// Copyright 2011 The Android Open Source Project
+//
+// Cache manager for pre-processed PNG files.
+// Contains code for managing which PNG files get processed
+// at build time.
+//
+
+#ifndef CRUNCHCACHE_H
+#define CRUNCHCACHE_H
+
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include "FileFinder.h"
+#include "CacheUpdater.h"
+
+using namespace android;
+
+/** CrunchCache
+ *  This class is a cache manager which can pre-process PNG files and store
+ *  them in a mirror-cache. It's capable of doing incremental updates to its
+ *  cache.
+ *
+ *  Usage:
+ *      Create an instance initialized with the root of the source tree, the
+ *      root location to store the cache files, and an instance of a file finder.
+ *      Then update the cache by calling crunch.
+ */
+class CrunchCache {
+public:
+    // Constructor
+    CrunchCache(String8 sourcePath, String8 destPath, FileFinder* ff);
+
+    // Nobody should be calling the default constructor
+    // So this space is intentionally left blank
+
+    // Default Copy Constructor and Destructor are fine
+
+    /** crunch is the workhorse of this class.
+     * It goes through all the files found in the sourcePath and compares
+     * them to the cached versions in the destPath. If the optional
+     * argument forceOverwrite is set to true, then all source files are
+     * re-crunched even if they have not been modified recently. Otherwise,
+     * source files are only crunched when they needUpdating. Afterwards,
+     * we delete any leftover files in the cache that are no longer present
+     * in source.
+     *
+     * PRECONDITIONS:
+     *      No setup besides construction is needed
+     * POSTCONDITIONS:
+     *      The cache is updated to fully reflect all changes in source.
+     *      The function then returns the number of files changed in cache
+     *      (counting deletions).
+     */
+    size_t crunch(CacheUpdater* cu, bool forceOverwrite=false);
+
+private:
+    /** loadFiles is a wrapper to the FileFinder that places matching
+     * files into mSourceFiles and mDestFiles.
+     *
+     *  POSTCONDITIONS
+     *      mDestFiles and mSourceFiles are refreshed to reflect the current
+     *      state of the files in the source and dest directories.
+     *      Any previous contents of mSourceFiles and mDestFiles are cleared.
+     */
+    void loadFiles();
+
+    /** needsUpdating takes a file path
+     * and returns true if the file represented by this path is newer in the
+     * sourceFiles than in the cache (mDestFiles).
+     *
+     * PRECONDITIONS:
+     *      mSourceFiles and mDestFiles must be initialized and filled.
+     * POSTCONDITIONS:
+     *      returns true if and only if source file's modification time
+     *      is greater than the cached file's mod-time. Otherwise returns false.
+     *
+     * USAGE:
+     *      Should be used something like the following:
+     *      if (needsUpdating(filePath))
+     *          // Recrunch sourceFile out to destFile.
+     *
+     */
+    bool needsUpdating(String8 relativePath) const;
+
+    // DATA MEMBERS ====================================================
+
+    String8 mSourcePath;
+    String8 mDestPath;
+
+    Vector<String8> mExtensions;
+
+    // Each vector of paths contains one entry per PNG file encountered.
+    // Each entry consists of a path pointing to that PNG.
+    DefaultKeyedVector<String8,time_t> mSourceFiles;
+    DefaultKeyedVector<String8,time_t> mDestFiles;
+
+    // Pointer to a FileFinder to use
+    FileFinder* mFileFinder;
+};
+
+#endif // CRUNCHCACHE_H
diff --git a/DirectoryWalker.h b/DirectoryWalker.h
new file mode 100644 (file)
index 0000000..88031d0
--- /dev/null
@@ -0,0 +1,98 @@
+//
+// Copyright 2011 The Android Open Source Project
+//
+// Defines an abstraction for opening a directory on the filesystem and
+// iterating through it.
+
+#ifndef DIRECTORYWALKER_H
+#define DIRECTORYWALKER_H
+
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <utils/String8.h>
+
+#include <stdio.h>
+
+using namespace android;
+
+// Directory Walker
+// This is an abstraction for walking through a directory and getting files
+// and descriptions.
+
+class DirectoryWalker {
+public:
+    virtual ~DirectoryWalker() {};
+    virtual bool openDir(String8 path) = 0;
+    virtual bool openDir(const char* path) = 0;
+    // Advance to next directory entry
+    virtual struct dirent* nextEntry() = 0;
+    // Get the stats for the current entry
+    virtual struct stat*   entryStats() = 0;
+    // Clean Up
+    virtual void closeDir() = 0;
+    // This class is able to replicate itself on the heap
+    virtual DirectoryWalker* clone() = 0;
+
+    // DATA MEMBERS
+    // Current directory entry
+    struct dirent mEntry;
+    // Stats for that directory entry
+    struct stat mStats;
+    // Base path
+    String8 mBasePath;
+};
+
+// System Directory Walker
+// This is an implementation of the above abstraction that calls
+// real system calls and is fully functional.
+// functions are inlined since they're very short and simple
+
+class SystemDirectoryWalker : public DirectoryWalker {
+
+    // Default constructor, copy constructor, and destructor are fine
+public:
+    virtual bool openDir(String8 path) {
+        mBasePath = path;
+        dir = NULL;
+        dir = opendir(mBasePath.string() );
+
+        if (dir == NULL)
+            return false;
+
+        return true;
+    };
+    virtual bool openDir(const char* path) {
+        String8 p(path);
+        openDir(p);
+        return true;
+    };
+    // Advance to next directory entry
+    virtual struct dirent* nextEntry() {
+        struct dirent* entryPtr = readdir(dir);
+        if (entryPtr == NULL)
+            return NULL;
+
+        mEntry = *entryPtr;
+        // Get stats
+        String8 fullPath = mBasePath.appendPathCopy(mEntry.d_name);
+        stat(fullPath.string(),&mStats);
+        return &mEntry;
+    };
+    // Get the stats for the current entry
+    virtual struct stat*   entryStats() {
+        return &mStats;
+    };
+    virtual void closeDir() {
+        closedir(dir);
+    };
+    virtual DirectoryWalker* clone() {
+        return new SystemDirectoryWalker(*this);
+    };
+private:
+    DIR* dir;
+};
+
+#endif // DIRECTORYWALKER_H
diff --git a/FileFinder.cpp b/FileFinder.cpp
new file mode 100644 (file)
index 0000000..18775c0
--- /dev/null
@@ -0,0 +1,98 @@
+//
+// Copyright 2011 The Android Open Source Project
+//
+
+// File Finder implementation.
+// Implementation for the functions declared and documented in FileFinder.h
+
+#include <utils/Vector.h>
+#include <utils/String8.h>
+#include <utils/KeyedVector.h>
+
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include "DirectoryWalker.h"
+#include "FileFinder.h"
+
+//#define DEBUG
+
+using android::String8;
+
+// Private function to check whether a file is a directory or not
+bool isDirectory(const char* filename) {
+    struct stat fileStat;
+    if (stat(filename, &fileStat) == -1) {
+        return false;
+    }
+    return(S_ISDIR(fileStat.st_mode));
+}
+
+
+// Private function to check whether a file is a regular file or not
+bool isFile(const char* filename) {
+    struct stat fileStat;
+    if (stat(filename, &fileStat) == -1) {
+        return false;
+    }
+    return(S_ISREG(fileStat.st_mode));
+}
+
+bool SystemFileFinder::findFiles(String8 basePath, Vector<String8>& extensions,
+                                 KeyedVector<String8,time_t>& fileStore,
+                                 DirectoryWalker* dw)
+{
+    // Scan the directory pointed to by basePath
+    // check files and recurse into subdirectories.
+    if (!dw->openDir(basePath)) {
+        return false;
+    }
+    /*
+     *  Go through all directory entries. Check each file using checkAndAddFile
+     *  and recurse into sub-directories.
+     */
+    struct dirent* entry;
+    while ((entry = dw->nextEntry()) != NULL) {
+        String8 entryName(entry->d_name);
+        if (entry->d_name[0] == '.') // Skip hidden files and directories
+            continue;
+
+        String8 fullPath = basePath.appendPathCopy(entryName);
+        // If this entry is a directory we'll recurse into it
+        if (isDirectory(fullPath.string()) ) {
+            DirectoryWalker* copy = dw->clone();
+            findFiles(fullPath, extensions, fileStore,copy);
+            delete copy;
+        }
+
+        // If this entry is a file, we'll pass it over to checkAndAddFile
+        if (isFile(fullPath.string()) ) {
+            checkAndAddFile(fullPath,dw->entryStats(),extensions,fileStore);
+        }
+    }
+
+    // Clean up
+    dw->closeDir();
+
+    return true;
+}
+
+void SystemFileFinder::checkAndAddFile(String8 path, const struct stat* stats,
+                                       Vector<String8>& extensions,
+                                       KeyedVector<String8,time_t>& fileStore)
+{
+    // Loop over the extensions, checking for a match
+    bool done = false;
+    String8 ext(path.getPathExtension());
+    ext.toLower();
+    for (size_t i = 0; i < extensions.size() && !done; ++i) {
+        String8 ext2 = extensions[i].getPathExtension();
+        ext2.toLower();
+        // Compare the extensions. If a match is found, add to storage.
+        if (ext == ext2) {
+            done = true;
+            fileStore.add(path,stats->st_mtime);
+        }
+    }
+}
+
diff --git a/FileFinder.h b/FileFinder.h
new file mode 100644 (file)
index 0000000..6974aee
--- /dev/null
@@ -0,0 +1,80 @@
+//
+// Copyright 2011 The Android Open Source Project
+//
+
+// File Finder.
+// This is a collection of useful functions for finding paths and modification
+// times of files that match an extension pattern in a directory tree.
+// and finding files in it.
+
+#ifndef FILEFINDER_H
+#define FILEFINDER_H
+
+#include <utils/Vector.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+
+#include "DirectoryWalker.h"
+
+using namespace android;
+
+// Abstraction to allow for dependency injection. See MockFileFinder.h
+// for the testing implementation.
+class FileFinder {
+public:
+    virtual bool findFiles(String8 basePath, Vector<String8>& extensions,
+                           KeyedVector<String8,time_t>& fileStore,
+                           DirectoryWalker* dw) = 0;
+
+    virtual ~FileFinder() {};
+};
+
+class SystemFileFinder : public FileFinder {
+public:
+
+    /* findFiles takes a path, a Vector of extensions, and a destination KeyedVector
+     *           and places path/modification date key/values pointing to
+     *          all files with matching extensions found into the KeyedVector
+     * PRECONDITIONS
+     *     path is a valid system path
+     *     extensions should include leading "."
+     *                This is not necessary, but the comparison directly
+     *                compares the end of the path string so if the "."
+     *                is excluded there is a small chance you could have
+     *                a false positive match. (For example: extension "png"
+     *                would match a file called "blahblahpng")
+     *
+     * POSTCONDITIONS
+     *     fileStore contains (in no guaranteed order) paths to all
+     *                matching files encountered in subdirectories of path
+     *                as keys in the KeyedVector. Each key has the modification time
+     *                of the file as its value.
+     *
+     * Calls checkAndAddFile on each file encountered in the directory tree
+     * Recursively descends into subdirectories.
+     */
+    virtual bool findFiles(String8 basePath, Vector<String8>& extensions,
+                           KeyedVector<String8,time_t>& fileStore,
+                           DirectoryWalker* dw);
+
+private:
+    /**
+     * checkAndAddFile looks at a single file path and stat combo
+     * to determine whether it is a matching file (by looking at
+     * the extension)
+     *
+     * PRECONDITIONS
+     *    no setup is needed
+     *
+     * POSTCONDITIONS
+     *    If the given file has a matching extension then a new entry
+     *    is added to the KeyedVector with the path as the key and the modification
+     *    time as the value.
+     *
+     */
+    static void checkAndAddFile(String8 path, const struct stat* stats,
+                                Vector<String8>& extensions,
+                                KeyedVector<String8,time_t>& fileStore);
+
+};
+#endif // FILEFINDER_H
index 3c471ca76107cc49b5f8e3ca15e87725ed80faf9..ffbe875b72f772b66766c35aac8b9305a60b0e3a 100644 (file)
@@ -980,6 +980,10 @@ status_t preProcessImage(Bundle* bundle, const sp<AaptAssets>& assets,
 
     String8 printableName(file->getPrintableSource());
 
+    if (bundle->getVerbose()) {
+        printf("Processing image: %s\n", printableName.string());
+    }
+
     png_structp read_ptr = NULL;
     png_infop read_info = NULL;
     FILE* fp;
@@ -1080,7 +1084,132 @@ bail:
     return error;
 }
 
+status_t preProcessImageToCache(Bundle* bundle, String8 source, String8 dest)
+{
+    png_structp read_ptr = NULL;
+    png_infop read_info = NULL;
+
+    FILE* fp;
+
+    image_info imageInfo;
+
+    png_structp write_ptr = NULL;
+    png_infop write_info = NULL;
+
+    status_t error = UNKNOWN_ERROR;
+
+    if (bundle->getVerbose()) {
+        printf("Processing image to cache: %s => %s\n", source.string(), dest.string());
+    }
+
+    // Get a file handler to read from
+    fp = fopen(source.string(),"rb");
+    if (fp == NULL) {
+        fprintf(stderr, "%s ERROR: Unable to open PNG file\n", source.string());
+        return error;
+    }
+
+    // Call libpng to get a struct to read image data into
+    read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+    if (!read_ptr) {
+        fclose(fp);
+        png_destroy_read_struct(&read_ptr, &read_info,NULL);
+        return error;
+    }
+
+    // Call libpng to get a struct to read image info into
+    read_info = png_create_info_struct(read_ptr);
+    if (!read_info) {
+        fclose(fp);
+        png_destroy_read_struct(&read_ptr, &read_info,NULL);
+        return error;
+    }
+
+    // Set a jump point for libpng to long jump back to on error
+    if (setjmp(png_jmpbuf(read_ptr))) {
+        fclose(fp);
+        png_destroy_read_struct(&read_ptr, &read_info,NULL);
+        return error;
+    }
+
+    // Set up libpng to read from our file.
+    png_init_io(read_ptr,fp);
+
+    // Actually read data from the file
+    read_png(source.string(), read_ptr, read_info, &imageInfo);
+
+    // We're done reading so we can clean up
+    // Find old file size before releasing handle
+    fseek(fp, 0, SEEK_END);
+    size_t oldSize = (size_t)ftell(fp);
+    fclose(fp);
+    png_destroy_read_struct(&read_ptr, &read_info,NULL);
+
+    // Check to see if we're dealing with a 9-patch
+    // If we are, process appropriately
+    if (source.getBasePath().getPathExtension() == ".9")  {
+        if (do_9patch(source.string(), &imageInfo) != NO_ERROR) {
+            return error;
+        }
+    }
+
+    // Call libpng to create a structure to hold the processed image data
+    // that can be written to disk
+    write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+    if (!write_ptr) {
+        png_destroy_write_struct(&write_ptr, &write_info);
+        return error;
+    }
+
+    // Call libpng to create a structure to hold processed image info that can
+    // be written to disk
+    write_info = png_create_info_struct(write_ptr);
+    if (!write_info) {
+        png_destroy_write_struct(&write_ptr, &write_info);
+        return error;
+    }
+
+    // Open up our destination file for writing
+    fp = fopen(dest.string(), "wb");
+    if (!fp) {
+        fprintf(stderr, "%s ERROR: Unable to open PNG file\n", dest.string());
+        png_destroy_write_struct(&write_ptr, &write_info);
+        return error;
+    }
 
+    // Set up libpng to write to our file
+    png_init_io(write_ptr, fp);
+
+    // Set up a jump for libpng to long jump back on on errors
+    if (setjmp(png_jmpbuf(write_ptr))) {
+        fclose(fp);
+        png_destroy_write_struct(&write_ptr, &write_info);
+        return error;
+    }
+
+    // Actually write out to the new png
+    write_png(dest.string(), write_ptr, write_info, imageInfo,
+              bundle->getGrayscaleTolerance());
+
+    if (bundle->getVerbose()) {
+        // Find the size of our new file
+        FILE* reader = fopen(dest.string(), "rb");
+        fseek(reader, 0, SEEK_END);
+        size_t newSize = (size_t)ftell(reader);
+        fclose(reader);
+
+        float factor = ((float)newSize)/oldSize;
+        int percent = (int)(factor*100);
+        printf("  (processed image to cache entry %s: %d%% size of source)\n",
+               dest.string(), percent);
+    }
+
+    //Clean up
+    fclose(fp);
+    png_destroy_write_struct(&write_ptr, &write_info);
+
+    return NO_ERROR;
+}
 
 status_t postProcessImage(const sp<AaptAssets>& assets,
                           ResourceTable* table, const sp<AaptFile>& file)
index 168e22ff8b53a041f3d6706195a81fa1188f3c31..4816905572c9b034d4ec22dd9b0e32e0ac8081df 100644 (file)
--- a/Images.h
+++ b/Images.h
@@ -8,11 +8,19 @@
 #define IMAGES_H
 
 #include "ResourceTable.h"
+#include "Bundle.h"
+
+#include <utils/String8.h>
+#include <utils/RefBase.h>
+
+using android::String8;
 
 status_t preProcessImage(Bundle* bundle, const sp<AaptAssets>& assets,
                          const sp<AaptFile>& file, String8* outNewLeafName);
 
+status_t preProcessImageToCache(Bundle* bundle, String8 source, String8 dest);
+
 status_t postProcessImage(const sp<AaptAssets>& assets,
-                                                 ResourceTable* table, const sp<AaptFile>& file);
+                          ResourceTable* table, const sp<AaptFile>& file);
 
 #endif
index 1e63131eb5bfbe5ee7348a7991eff289a168a32c..50c828def46dd86178e0385d54c8d03f28387792 100644 (file)
--- a/Main.cpp
+++ b/Main.cpp
@@ -65,9 +65,10 @@ void usage(void)
         "        [--max-res-version VAL] \\\n"
         "        [-I base-package [-I base-package ...]] \\\n"
         "        [-A asset-source-dir]  [-G class-list-file] [-P public-definitions-file] \\\n"
-        "        [-S resource-sources [-S resource-sources ...]] "
+        "        [-S resource-sources [-S resource-sources ...]] \\\n"
         "        [-F apk-file] [-J R-file-dir] \\\n"
         "        [--product product1,product2,...] \\\n"
+        "        [-c CONFIGS] [--preferred-configurations CONFIGS] \\\n"
         "        [-o] \\\n"
         "        [raw-files-dir [raw-files-dir] ...]\n"
         "\n"
@@ -82,6 +83,9 @@ void usage(void)
     fprintf(stderr,
         " %s a[dd] [-v] file.{zip,jar,apk} file1 [file2 ...]\n"
         "   Add specified files to Zip-compatible archive.\n\n", gProgName);
+    fprintf(stderr,
+        " %s c[runch] [-v] -S resource-sources ... -C output-folder ...\n"
+        "   Do PNG preprocessing and store the results in output folder.\n\n", gProgName);
     fprintf(stderr,
         " %s v[ersion]\n"
         "   Print program version.\n\n", gProgName);
@@ -145,8 +149,16 @@ void usage(void)
         "       inserts android:versionName in to manifest.\n"
         "   --custom-package\n"
         "       generates R.java into a different package.\n"
+        "   --extra-packages\n"
+        "       generate R.java for libraries. Separate libraries with ':'.\n"
+        "   --generate-dependencies\n"
+        "       generate dependency files in the same directories for R.java and resource package\n"
         "   --auto-add-overlay\n"
         "       Automatically add resources that are only in overlays.\n"
+        "   --preferred-configurations\n"
+        "       Like the -c option for filtering out unneeded configurations, but\n"
+        "       only expresses a preference.  If there is no resource available with\n"
+        "       the preferred configuration then it will not be stripped.\n"
         "   --rename-manifest-package\n"
         "       Rewrite the manifest so that its package name is the package name\n"
         "       given here.  Relative class names (for example .Foo) will be\n"
@@ -186,6 +198,7 @@ int handleCommand(Bundle* bundle)
     case kCommandAdd:       return doAdd(bundle);
     case kCommandRemove:    return doRemove(bundle);
     case kCommandPackage:   return doPackage(bundle);
+    case kCommandCrunch:    return doCrunch(bundle);
     default:
         fprintf(stderr, "%s: requested command not yet supported\n", gProgName);
         return 1;
@@ -223,6 +236,8 @@ int main(int argc, char* const argv[])
         bundle.setCommand(kCommandRemove);
     else if (argv[1][0] == 'p')
         bundle.setCommand(kCommandPackage);
+    else if (argv[1][0] == 'c')
+        bundle.setCommand(kCommandCrunch);
     else {
         fprintf(stderr, "ERROR: Unknown command '%s'\n", argv[1]);
         wantUsage = true;
@@ -393,6 +408,17 @@ int main(int argc, char* const argv[])
                 convertPath(argv[0]);
                 bundle.addResourceSourceDir(argv[0]);
                 break;
+            case 'C':
+                argc--;
+                argv++;
+                if (!argc) {
+                    fprintf(stderr, "ERROR: No argument supplied for '-C' option\n");
+                    wantUsage = true;
+                    goto bail;
+                }
+                convertPath(argv[0]);
+                bundle.setCrunchedOutputDir(argv[0]);
+                break;
             case '0':
                 argc--;
                 argv++;
@@ -475,8 +501,28 @@ int main(int argc, char* const argv[])
                         goto bail;
                     }
                     bundle.setCustomPackage(argv[0]);
+                } else if (strcmp(cp, "-extra-packages") == 0) {
+                    argc--;
+                    argv++;
+                    if (!argc) {
+                        fprintf(stderr, "ERROR: No argument supplied for '--extra-packages' option\n");
+                        wantUsage = true;
+                        goto bail;
+                    }
+                    bundle.setExtraPackages(argv[0]);
+                } else if (strcmp(cp, "-generate-dependencies") == 0) {
+                    bundle.setGenDependencies(true);
                 } else if (strcmp(cp, "-utf16") == 0) {
                     bundle.setWantUTF16(true);
+                } else if (strcmp(cp, "-preferred-configurations") == 0) {
+                    argc--;
+                    argv++;
+                    if (!argc) {
+                        fprintf(stderr, "ERROR: No argument supplied for '--preferred-configurations' option\n");
+                        wantUsage = true;
+                        goto bail;
+                    }
+                    bundle.addPreferredConfigurations(argv[0]);
                 } else if (strcmp(cp, "-rename-manifest-package") == 0) {
                     argc--;
                     argv++;
@@ -508,7 +554,9 @@ int main(int argc, char* const argv[])
                     bundle.setProduct(argv[0]);
                 } else if (strcmp(cp, "-non-constant-id") == 0) {
                     bundle.setNonConstantId(true);
-                } else {
+                } else if (strcmp(cp, "-no-crunch") == 0) {
+                    bundle.setUseCrunchCache(true);
+                }else {
                     fprintf(stderr, "ERROR: Unknown option '-%s'\n", cp);
                     wantUsage = true;
                     goto bail;
diff --git a/Main.h b/Main.h
index 3ba4f39062f81aeb71a23d494ce65ad7432baa43..d20c601a0c7726dcc561f17ef78156fccdb0c066 100644 (file)
--- a/Main.h
+++ b/Main.h
 #include "AaptAssets.h"
 #include "ZipFile.h"
 
+
+/* Benchmarking Flag */
+//#define BENCHMARK 1
+
+#if BENCHMARK
+    #include <time.h>
+#endif /* BENCHMARK */
+
 extern int doVersion(Bundle* bundle);
 extern int doList(Bundle* bundle);
 extern int doDump(Bundle* bundle);
 extern int doAdd(Bundle* bundle);
 extern int doRemove(Bundle* bundle);
 extern int doPackage(Bundle* bundle);
+extern int doCrunch(Bundle* bundle);
 
 extern int calcPercent(long uncompressedLen, long compressedLen);
 
@@ -27,6 +36,8 @@ extern android::status_t writeAPK(Bundle* bundle,
     const sp<AaptAssets>& assets,
     const android::String8& outputFile);
 
+extern android::status_t updatePreProcessedCache(Bundle* bundle);
+
 extern android::status_t buildResources(Bundle* bundle,
     const sp<AaptAssets>& assets);
 
@@ -46,4 +57,6 @@ int dumpResources(Bundle* bundle);
 String8 getAttribute(const ResXMLTree& tree, const char* ns,
                             const char* attr, String8* outError);
 
+status_t writeDependencyPreReqs(Bundle* bundle, const sp<AaptAssets>& assets,
+                                FILE* fp, bool includeRaw);
 #endif // __MAIN_H
diff --git a/NOTICE b/NOTICE
new file mode 100644 (file)
index 0000000..c5b1efa
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, 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.
+
+   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.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
index 999a5cf82222a4ac6621c0920ea8b10155a28e00..3930117b67a2126a2fca50498cac1a523e01a3db 100644 (file)
@@ -6,6 +6,7 @@
 #include "Main.h"
 #include "AaptAssets.h"
 #include "ResourceTable.h"
+#include "ResourceFilter.h"
 
 #include <utils/Log.h>
 #include <utils/threads.h>
@@ -33,8 +34,8 @@ static const char* kNoCompressExt[] = {
 
 /* fwd decls, so I can write this downward */
 ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptAssets>& assets);
-ssize_t processAssets(Bundle* bundle, ZipFile* zip,
-                        const sp<AaptDir>& dir, const AaptGroupEntry& ge);
+ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptDir>& dir,
+                        const AaptGroupEntry& ge, const ResourceFilter* filter);
 bool processFile(Bundle* bundle, ZipFile* zip,
                         const sp<AaptGroup>& group, const sp<AaptFile>& file);
 bool okayToCompress(Bundle* bundle, const String8& pathName);
@@ -50,6 +51,11 @@ ssize_t processJarFiles(Bundle* bundle, ZipFile* zip);
 status_t writeAPK(Bundle* bundle, const sp<AaptAssets>& assets,
                        const String8& outputFile)
 {
+    #if BENCHMARK
+    fprintf(stdout, "BENCHMARK: Starting APK Bundling \n");
+    long startAPKTime = clock();
+    #endif /* BENCHMARK */
+
     status_t result = NO_ERROR;
     ZipFile* zip = NULL;
     int count;
@@ -172,6 +178,21 @@ status_t writeAPK(Bundle* bundle, const sp<AaptAssets>& assets,
         }
     }
 
+    // If we've been asked to generate a dependency file for the .ap_ package,
+    // do so here
+    if (bundle->getGenDependencies()) {
+        // The dependency file gets output to the same directory
+        // as the specified output file with an additional .d extension.
+        // e.g. bin/resources.ap_.d
+        String8 dependencyFile = outputFile;
+        dependencyFile.append(".d");
+
+        FILE* fp = fopen(dependencyFile.string(), "a");
+        // Add this file to the dependency file
+        fprintf(fp, "%s \\\n", outputFile.string());
+        fclose(fp);
+    }
+
     assert(result == NO_ERROR);
 
 bail:
@@ -187,6 +208,10 @@ bail:
 
     if (result == NO_ERROR && bundle->getVerbose())
         printf("Done!\n");
+
+    #if BENCHMARK
+    fprintf(stdout, "BENCHMARK: End APK Bundling. Time Elapsed: %f ms \n",(clock() - startAPKTime)/1000.0);
+    #endif /* BENCHMARK */
     return result;
 }
 
@@ -204,34 +229,45 @@ ssize_t processAssets(Bundle* bundle, ZipFile* zip,
     const size_t N = assets->getGroupEntries().size();
     for (size_t i=0; i<N; i++) {
         const AaptGroupEntry& ge = assets->getGroupEntries()[i];
-        if (!filter.match(ge.toParams())) {
-            continue;
-        }
-        ssize_t res = processAssets(bundle, zip, assets, ge);
+
+        ssize_t res = processAssets(bundle, zip, assets, ge, &filter);
         if (res < 0) {
             return res;
         }
+
         count += res;
     }
 
     return count;
 }
 
-ssize_t processAssets(Bundle* bundle, ZipFile* zip,
-                      const sp<AaptDir>& dir, const AaptGroupEntry& ge)
+ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptDir>& dir,
+        const AaptGroupEntry& ge, const ResourceFilter* filter)
 {
     ssize_t count = 0;
 
     const size_t ND = dir->getDirs().size();
     size_t i;
     for (i=0; i<ND; i++) {
-        ssize_t res = processAssets(bundle, zip, dir->getDirs().valueAt(i), ge);
+        const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
+
+        const bool filterable = filter != NULL && subDir->getLeaf().find("mipmap-") != 0;
+
+        if (filterable && subDir->getLeaf() != subDir->getPath() && !filter->match(ge.toParams())) {
+            continue;
+        }
+
+        ssize_t res = processAssets(bundle, zip, subDir, ge, filterable ? filter : NULL);
         if (res < 0) {
             return res;
         }
         count += res;
     }
 
+    if (filter != NULL && !filter->match(ge.toParams())) {
+        return count;
+    }
+
     const size_t NF = dir->getFiles().size();
     for (i=0; i<NF; i++) {
         sp<AaptGroup> gp = dir->getFiles().valueAt(i);
@@ -441,7 +477,7 @@ ssize_t processJarFile(ZipFile* jar, ZipFile* out)
 
 ssize_t processJarFiles(Bundle* bundle, ZipFile* zip)
 {
-    ssize_t err;
+    status_t err;
     ssize_t count = 0;
     const android::Vector<const char*>& jars = bundle->getJarFiles();
 
@@ -450,7 +486,7 @@ ssize_t processJarFiles(Bundle* bundle, ZipFile* zip)
         ZipFile jar;
         err = jar.open(jars[i], ZipFile::kOpenReadOnly);
         if (err != 0) {
-            fprintf(stderr, "ERROR: unable to open '%s' as a zip file: %zd\n",
+            fprintf(stderr, "ERROR: unable to open '%s' as a zip file: %d\n",
                 jars[i], err);
             return err;
         }
index 0a4f24f26df217860a9c3bea381debae888bfd9f..1ecf7daef123edad3147a9a5f848bffd1915bdc2 100644 (file)
 #include "ResourceTable.h"
 #include "Images.h"
 
+#include "CrunchCache.h"
+#include "FileFinder.h"
+#include "CacheUpdater.h"
+
+#if HAVE_PRINTF_ZD
+#  define ZD "%zd"
+#  define ZD_TYPE ssize_t
+#else
+#  define ZD "%ld"
+#  define ZD_TYPE long
+#endif
+
 #define NOISY(x) // x
 
 // ==========================================================================
@@ -51,6 +63,12 @@ ResourceTypeSet::ResourceTypeSet()
 {
 }
 
+FilePathStore::FilePathStore()
+    :RefBase(),
+     Vector<String8>()
+{
+}
+
 class ResourceDirIterator
 {
 public:
@@ -148,9 +166,10 @@ private:
 
 bool isValidResourceType(const String8& type)
 {
-    return type == "anim" || type == "drawable" || type == "layout"
+    return type == "anim" || type == "animator" || type == "interpolator"
+        || type == "drawable" || type == "layout"
         || type == "values" || type == "xml" || type == "raw"
-        || type == "color" || type == "menu";
+        || type == "color" || type == "menu" || type == "mipmap";
 }
 
 static sp<AaptFile> getResourceFile(const sp<AaptAssets>& assets, bool makeIfNecessary=true)
@@ -284,20 +303,21 @@ static status_t makeFileResources(Bundle* bundle, const sp<AaptAssets>& assets,
 }
 
 static status_t preProcessImages(Bundle* bundle, const sp<AaptAssets>& assets,
-                          const sp<ResourceTypeSet>& set)
+                          const sp<ResourceTypeSet>& set, const char* type)
 {
-    ResourceDirIterator it(set, String8("drawable"));
-    Vector<sp<AaptFile> > newNameFiles;
-    Vector<String8> newNamePaths;
     bool hasErrors = false;
-    ssize_t res;
-    while ((res=it.next()) == NO_ERROR) {
-        res = preProcessImage(bundle, assets, it.getFile(), NULL);
-        if (res < NO_ERROR) {
-            hasErrors = true;
+    ssize_t res = NO_ERROR;
+    if (bundle->getUseCrunchCache() == false) {
+        ResourceDirIterator it(set, String8(type));
+        Vector<sp<AaptFile> > newNameFiles;
+        Vector<String8> newNamePaths;
+        while ((res=it.next()) == NO_ERROR) {
+            res = preProcessImage(bundle, assets, it.getFile(), NULL);
+            if (res < NO_ERROR) {
+                hasErrors = true;
+            }
         }
     }
-
     return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR;
 }
 
@@ -340,18 +360,27 @@ static void collect_files(const sp<AaptDir>& dir,
 
         if (index < 0) {
             sp<ResourceTypeSet> set = new ResourceTypeSet();
+            NOISY(printf("Creating new resource type set for leaf %s with group %s (%p)\n",
+                    leafName.string(), group->getPath().string(), group.get()));
             set->add(leafName, group);
             resources->add(resType, set);
         } else {
             sp<ResourceTypeSet> set = resources->valueAt(index);
             index = set->indexOfKey(leafName);
             if (index < 0) {
+                NOISY(printf("Adding to resource type set for leaf %s group %s (%p)\n",
+                        leafName.string(), group->getPath().string(), group.get()));
                 set->add(leafName, group);
             } else {
                 sp<AaptGroup> existingGroup = set->valueAt(index);
-                int M = files.size();
-                for (int j=0; j<M; j++) {
-                    existingGroup->addFile(files.valueAt(j));
+                NOISY(printf("Extending to resource type set for leaf %s group %s (%p)\n",
+                        leafName.string(), group->getPath().string(), group.get()));
+                for (size_t j=0; j<files.size(); j++) {
+                    NOISY(printf("Adding file %s in group %s resType %s\n",
+                        files.valueAt(j)->getSourceFile().string(),
+                        files.keyAt(j).toDirName(String8()).string(),
+                        resType.string()));
+                    status_t err = existingGroup->addFile(files.valueAt(j));
                 }
             }
         }
@@ -366,9 +395,12 @@ static void collect_files(const sp<AaptAssets>& ass,
 
     for (int i=0; i<N; i++) {
         sp<AaptDir> d = dirs.itemAt(i);
+        NOISY(printf("Collecting dir #%d %p: %s, leaf %s\n", i, d.get(), d->getPath().string(),
+                d->getLeaf().string()));
         collect_files(d, resources);
 
         // don't try to include the res dir
+        NOISY(printf("Removing dir leaf %s\n", d->getLeaf().string()));
         ass->removeDir(d->getLeaf());
     }
 }
@@ -542,11 +574,11 @@ static bool applyFileOverlay(Bundle *bundle,
                         DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > baseFiles =
                                 baseGroup->getFiles();
                         for (size_t i=0; i < baseFiles.size(); i++) {
-                            printf("baseFile %ld has flavor %s\n", i,
+                            printf("baseFile " ZD " has flavor %s\n", (ZD_TYPE) i,
                                     baseFiles.keyAt(i).toString().string());
                         }
                         for (size_t i=0; i < overlayFiles.size(); i++) {
-                            printf("overlayFile %ld has flavor %s\n", i,
+                            printf("overlayFile " ZD " has flavor %s\n", (ZD_TYPE) i,
                                     overlayFiles.keyAt(i).toString().string());
                         }
                     }
@@ -558,16 +590,21 @@ static bool applyFileOverlay(Bundle *bundle,
                         size_t baseFileIndex =
                                 baseGroup->getFiles().indexOfKey(overlayFiles.
                                 keyAt(overlayGroupIndex));
-                        if(baseFileIndex < UNKNOWN_ERROR) {
+                        if (baseFileIndex < UNKNOWN_ERROR) {
                             if (bundle->getVerbose()) {
-                                printf("found a match (%ld) for overlay file %s, for flavor %s\n",
-                                        baseFileIndex,
+                                printf("found a match (" ZD ") for overlay file %s, for flavor %s\n",
+                                        (ZD_TYPE) baseFileIndex,
                                         overlayGroup->getLeaf().string(),
                                         overlayFiles.keyAt(overlayGroupIndex).toString().string());
                             }
                             baseGroup->removeFile(baseFileIndex);
                         } else {
                             // didn't find a match fall through and add it..
+                            if (true || bundle->getVerbose()) {
+                                printf("nothing matches overlay file %s, for flavor %s\n",
+                                        overlayGroup->getLeaf().string(),
+                                        overlayFiles.keyAt(overlayGroupIndex).toString().string());
+                            }
                         }
                         baseGroup->addFile(overlayFiles.valueAt(overlayGroupIndex));
                         assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex));
@@ -747,6 +784,35 @@ status_t massageManifest(Bundle* bundle, sp<XMLNode> root)
             } \
         } while (0)
 
+status_t updatePreProcessedCache(Bundle* bundle)
+{
+    #if BENCHMARK
+    fprintf(stdout, "BENCHMARK: Starting PNG PreProcessing \n");
+    long startPNGTime = clock();
+    #endif /* BENCHMARK */
+
+    String8 source(bundle->getResourceSourceDirs()[0]);
+    String8 dest(bundle->getCrunchedOutputDir());
+
+    FileFinder* ff = new SystemFileFinder();
+    CrunchCache cc(source,dest,ff);
+
+    CacheUpdater* cu = new SystemCacheUpdater(bundle);
+    size_t numFiles = cc.crunch(cu);
+
+    if (bundle->getVerbose())
+        fprintf(stdout, "Crunched %d PNG files to update cache\n", (int)numFiles);
+
+    delete ff;
+    delete cu;
+
+    #if BENCHMARK
+    fprintf(stdout, "BENCHMARK: End PNG PreProcessing. Time Elapsed: %f ms \n"
+            ,(clock() - startPNGTime)/1000.0);
+    #endif /* BENCHMARK */
+    return 0;
+}
+
 status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
 {
     // First, look for a package file to parse.  This is required to
@@ -798,18 +864,24 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
     sp<ResourceTypeSet> drawables;
     sp<ResourceTypeSet> layouts;
     sp<ResourceTypeSet> anims;
+    sp<ResourceTypeSet> animators;
+    sp<ResourceTypeSet> interpolators;
     sp<ResourceTypeSet> xmls;
     sp<ResourceTypeSet> raws;
     sp<ResourceTypeSet> colors;
     sp<ResourceTypeSet> menus;
+    sp<ResourceTypeSet> mipmaps;
 
     ASSIGN_IT(drawable);
     ASSIGN_IT(layout);
     ASSIGN_IT(anim);
+    ASSIGN_IT(animator);
+    ASSIGN_IT(interpolator);
     ASSIGN_IT(xml);
     ASSIGN_IT(raw);
     ASSIGN_IT(color);
     ASSIGN_IT(menu);
+    ASSIGN_IT(mipmap);
 
     assets->setResources(resources);
     // now go through any resource overlays and collect their files
@@ -825,10 +897,13 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
     if (!applyFileOverlay(bundle, assets, &drawables, "drawable") ||
             !applyFileOverlay(bundle, assets, &layouts, "layout") ||
             !applyFileOverlay(bundle, assets, &anims, "anim") ||
+            !applyFileOverlay(bundle, assets, &animators, "animator") ||
+            !applyFileOverlay(bundle, assets, &interpolators, "interpolator") ||
             !applyFileOverlay(bundle, assets, &xmls, "xml") ||
             !applyFileOverlay(bundle, assets, &raws, "raw") ||
             !applyFileOverlay(bundle, assets, &colors, "color") ||
-            !applyFileOverlay(bundle, assets, &menus, "menu")) {
+            !applyFileOverlay(bundle, assets, &menus, "menu") ||
+            !applyFileOverlay(bundle, assets, &mipmaps, "mipmap")) {
         return UNKNOWN_ERROR;
     }
 
@@ -836,7 +911,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
 
     if (drawables != NULL) {
         if (bundle->getOutputAPKFile() != NULL) {
-            err = preProcessImages(bundle, assets, drawables);
+            err = preProcessImages(bundle, assets, drawables, "drawable");
         }
         if (err == NO_ERROR) {
             err = makeFileResources(bundle, assets, &table, drawables, "drawable");
@@ -848,6 +923,20 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
         }
     }
 
+    if (mipmaps != NULL) {
+        if (bundle->getOutputAPKFile() != NULL) {
+            err = preProcessImages(bundle, assets, mipmaps, "mipmap");
+        }
+        if (err == NO_ERROR) {
+            err = makeFileResources(bundle, assets, &table, mipmaps, "mipmap");
+            if (err != NO_ERROR) {
+                hasErrors = true;
+            }
+        } else {
+            hasErrors = true;
+        }
+    }
+
     if (layouts != NULL) {
         err = makeFileResources(bundle, assets, &table, layouts, "layout");
         if (err != NO_ERROR) {
@@ -862,6 +951,20 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
         }
     }
 
+    if (animators != NULL) {
+        err = makeFileResources(bundle, assets, &table, animators, "animator");
+        if (err != NO_ERROR) {
+            hasErrors = true;
+        }
+    }
+
+    if (interpolators != NULL) {
+        err = makeFileResources(bundle, assets, &table, interpolators, "interpolator");
+        if (err != NO_ERROR) {
+            hasErrors = true;
+        }
+    }
+
     if (xmls != NULL) {
         err = makeFileResources(bundle, assets, &table, xmls, "xml");
         if (err != NO_ERROR) {
@@ -969,6 +1072,36 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
         err = NO_ERROR;
     }
 
+    if (animators != NULL) {
+        ResourceDirIterator it(animators, String8("animator"));
+        while ((err=it.next()) == NO_ERROR) {
+            err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
+            if (err != NO_ERROR) {
+                hasErrors = true;
+            }
+        }
+
+        if (err < NO_ERROR) {
+            hasErrors = true;
+        }
+        err = NO_ERROR;
+    }
+
+    if (interpolators != NULL) {
+        ResourceDirIterator it(interpolators, String8("interpolator"));
+        while ((err=it.next()) == NO_ERROR) {
+            err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
+            if (err != NO_ERROR) {
+                hasErrors = true;
+            }
+        }
+
+        if (err < NO_ERROR) {
+            hasErrors = true;
+        }
+        err = NO_ERROR;
+    }
+
     if (xmls != NULL) {
         ResourceDirIterator it(xmls, String8("xml"));
         while ((err=it.next()) == NO_ERROR) {
@@ -1849,6 +1982,18 @@ status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets,
             return err;
         }
         fclose(fp);
+
+        // If we were asked to generate a dependency file, we'll go ahead and add this R.java
+        // as a target in the dependency file right next to it.
+        if (bundle->getGenDependencies()) {
+            // Add this R.java to the dependency file
+            String8 dependencyFile(bundle->getRClassDir());
+            dependencyFile.appendPath("R.java.d");
+
+            fp = fopen(dependencyFile.string(), "a");
+            fprintf(fp,"%s \\\n", dest.string());
+            fclose(fp);
+        }
     }
 
     return NO_ERROR;
@@ -2088,12 +2233,13 @@ writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
     // tag:attribute pairs that should be checked in layout files.
     KeyedVector<String8, NamespaceAttributePair> kLayoutTagAttrPairs;
     addTagAttrPair(&kLayoutTagAttrPairs, "view", NULL, "class");
+    addTagAttrPair(&kLayoutTagAttrPairs, "fragment", NULL, "class");
     addTagAttrPair(&kLayoutTagAttrPairs, "fragment", RESOURCES_ANDROID_NAMESPACE, "name");
 
     // tag:attribute pairs that should be checked in xml files.
     KeyedVector<String8, NamespaceAttributePair> kXmlTagAttrPairs;
     addTagAttrPair(&kXmlTagAttrPairs, "PreferenceScreen", RESOURCES_ANDROID_NAMESPACE, "fragment");
-    addTagAttrPair(&kXmlTagAttrPairs, "Header", RESOURCES_ANDROID_NAMESPACE, "fragment");
+    addTagAttrPair(&kXmlTagAttrPairs, "header", RESOURCES_ANDROID_NAMESPACE, "fragment");
 
     const Vector<sp<AaptDir> >& dirs = assets->resDirs();
     const size_t K = dirs.size();
@@ -2125,6 +2271,11 @@ writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
             }
         }
     }
+    // Handle the overlays
+    sp<AaptAssets> overlay = assets->getOverlay();
+    if (overlay.get()) {
+        return writeProguardForLayouts(keep, overlay);
+    }
     return NO_ERROR;
 }
 
@@ -2170,3 +2321,27 @@ writeProguardFile(Bundle* bundle, const sp<AaptAssets>& assets)
 
     return err;
 }
+
+// Loops through the string paths and writes them to the file pointer
+// Each file path is written on its own line with a terminating backslash.
+status_t writePathsToFile(const sp<FilePathStore>& files, FILE* fp)
+{
+    status_t deps = -1;
+    for (size_t file_i = 0; file_i < files->size(); ++file_i) {
+        // Add the full file path to the dependency file
+        fprintf(fp, "%s \\\n", files->itemAt(file_i).string());
+        deps++;
+    }
+    return deps;
+}
+
+status_t
+writeDependencyPreReqs(Bundle* bundle, const sp<AaptAssets>& assets, FILE* fp, bool includeRaw)
+{
+    status_t deps = -1;
+    deps += writePathsToFile(assets->getFullResPaths(), fp);
+    if (includeRaw) {
+        deps += writePathsToFile(assets->getFullAssetPaths(), fp);
+    }
+    return deps;
+}
diff --git a/ResourceFilter.cpp b/ResourceFilter.cpp
new file mode 100644 (file)
index 0000000..8cfd2a5
--- /dev/null
@@ -0,0 +1,112 @@
+//
+// Copyright 2011 The Android Open Source Project
+//
+// Build resource files from raw assets.
+//
+
+#include "ResourceFilter.h"
+
+status_t
+ResourceFilter::parse(const char* arg)
+{
+    if (arg == NULL) {
+        return 0;
+    }
+
+    const char* p = arg;
+    const char* q;
+
+    while (true) {
+        q = strchr(p, ',');
+        if (q == NULL) {
+            q = p + strlen(p);
+        }
+
+        String8 part(p, q-p);
+
+        if (part == "zz_ZZ") {
+            mContainsPseudo = true;
+        }
+        int axis;
+        uint32_t value;
+        if (AaptGroupEntry::parseNamePart(part, &axis, &value)) {
+            fprintf(stderr, "Invalid configuration: %s\n", arg);
+            fprintf(stderr, "                       ");
+            for (int i=0; i<p-arg; i++) {
+                fprintf(stderr, " ");
+            }
+            for (int i=0; i<q-p; i++) {
+                fprintf(stderr, "^");
+            }
+            fprintf(stderr, "\n");
+            return 1;
+        }
+
+        ssize_t index = mData.indexOfKey(axis);
+        if (index < 0) {
+            mData.add(axis, SortedVector<uint32_t>());
+        }
+        SortedVector<uint32_t>& sv = mData.editValueFor(axis);
+        sv.add(value);
+        // if it's a locale with a region, also match an unmodified locale of the
+        // same language
+        if (axis == AXIS_LANGUAGE) {
+            if (value & 0xffff0000) {
+                sv.add(value & 0x0000ffff);
+            }
+        }
+        p = q;
+        if (!*p) break;
+        p++;
+    }
+
+    return NO_ERROR;
+}
+
+bool
+ResourceFilter::isEmpty() const
+{
+    return mData.size() == 0;
+}
+
+bool
+ResourceFilter::match(int axis, uint32_t value) const
+{
+    if (value == 0) {
+        // they didn't specify anything so take everything
+        return true;
+    }
+    ssize_t index = mData.indexOfKey(axis);
+    if (index < 0) {
+        // we didn't request anything on this axis so take everything
+        return true;
+    }
+    const SortedVector<uint32_t>& sv = mData.valueAt(index);
+    return sv.indexOf(value) >= 0;
+}
+
+bool
+ResourceFilter::match(int axis, const ResTable_config& config) const
+{
+    return match(axis, AaptGroupEntry::getConfigValueForAxis(config, axis));
+}
+
+bool
+ResourceFilter::match(const ResTable_config& config) const
+{
+    for (int i=AXIS_START; i<=AXIS_END; i++) {
+        if (!match(i, AaptGroupEntry::getConfigValueForAxis(config, i))) {
+            return false;
+        }
+    }
+    return true;
+}
+
+const SortedVector<uint32_t>* ResourceFilter::configsForAxis(int axis) const
+{
+    ssize_t index = mData.indexOfKey(axis);
+    if (index < 0) {
+        return NULL;
+    }
+    return &mData.valueAt(index);
+}
diff --git a/ResourceFilter.h b/ResourceFilter.h
new file mode 100644 (file)
index 0000000..647b7bb
--- /dev/null
@@ -0,0 +1,33 @@
+//
+// Copyright 2011 The Android Open Source Project
+//
+// Build resource files from raw assets.
+//
+
+#ifndef RESOURCE_FILTER_H
+#define RESOURCE_FILTER_H
+
+#include "AaptAssets.h"
+
+/**
+ * Implements logic for parsing and handling "-c" and "--preferred-configurations"
+ * options.
+ */
+class ResourceFilter
+{
+public:
+    ResourceFilter() : mData(), mContainsPseudo(false) {}
+    status_t parse(const char* arg);
+    bool isEmpty() const;
+    bool match(int axis, uint32_t value) const;
+    bool match(int axis, const ResTable_config& config) const;
+    bool match(const ResTable_config& config) const;
+    const SortedVector<uint32_t>* configsForAxis(int axis) const;
+    inline bool containsPseudo() const { return mContainsPseudo; }
+
+private:
+    KeyedVector<int,SortedVector<uint32_t> > mData;
+    bool mContainsPseudo;
+};
+
+#endif
index 6459d0e8b66352568699dc3e6d980c5c6955df99..fdb39ca08e9f555a70dc9ef4babd30952d575c05 100644 (file)
@@ -7,6 +7,7 @@
 #include "ResourceTable.h"
 
 #include "XMLNode.h"
+#include "ResourceFilter.h"
 
 #include <utils/ByteOrder.h>
 #include <utils/ResourceTypes.h>
@@ -695,7 +696,7 @@ status_t parseAndAddEntry(Bundle* bundle,
             if (isInProductList(product, String16(bundleProduct))) {
                 ;
             } else if (strcmp16(String16("default").string(), product.string()) == 0 &&
-                       !outTable->hasBagOrEntry(myPackage, curType, ident)) {
+                       !outTable->hasBagOrEntry(myPackage, curType, ident, config)) {
                 ;
             } else {
                 return NO_ERROR;
@@ -1823,6 +1824,37 @@ bool ResourceTable::hasBagOrEntry(const String16& package,
     return false;
 }
 
+bool ResourceTable::hasBagOrEntry(const String16& package,
+                                  const String16& type,
+                                  const String16& name,
+                                  const ResTable_config& config) const
+{
+    // First look for this in the included resources...
+    uint32_t rid = mAssets->getIncludedResources()
+        .identifierForName(name.string(), name.size(),
+                           type.string(), type.size(),
+                           package.string(), package.size());
+    if (rid != 0) {
+        return true;
+    }
+
+    sp<Package> p = mPackages.valueFor(package);
+    if (p != NULL) {
+        sp<Type> t = p->getTypes().valueFor(type);
+        if (t != NULL) {
+            sp<ConfigList> c =  t->getConfigs().valueFor(name);
+            if (c != NULL) {
+                sp<Entry> e = c->getEntries().valueFor(config);
+                if (e != NULL) {
+                    return true;
+                }
+            }
+        }
+    }
+
+    return false;
+}
+
 bool ResourceTable::hasBagOrEntry(const String16& ref,
                                   const String16* defType,
                                   const String16* defPackage)
@@ -1963,10 +1995,11 @@ uint32_t ResourceTable::getResId(const String16& ref,
                                  bool onlyPublic) const
 {
     String16 package, type, name;
+    bool refOnlyPublic = true;
     if (!ResTable::expandResourceRef(
         ref.string(), ref.size(), &package, &type, &name,
         defType, defPackage ? defPackage:&mAssetsPackage,
-        outErrorMsg)) {
+        outErrorMsg, &refOnlyPublic)) {
         NOISY(printf("Expanding resource: ref=%s\n",
                      String8(ref).string()));
         NOISY(printf("Expanding resource: defType=%s\n",
@@ -1979,7 +2012,7 @@ uint32_t ResourceTable::getResId(const String16& ref,
                      String8(name).string()));
         return 0;
     }
-    uint32_t res = getResId(package, type, name, onlyPublic);
+    uint32_t res = getResId(package, type, name, onlyPublic && refOnlyPublic);
     NOISY(printf("Expanded resource: p=%s, t=%s, n=%s, res=%d\n",
                  String8(package).string(), String8(type).string(),
                  String8(name).string(), res));
@@ -2444,7 +2477,7 @@ ResourceTable::validateLocalizations(void)
         if (configSet.count(defaultLocale) == 0) {
             fprintf(stdout, "aapt: warning: string '%s' has no default translation in %s; found:",
                     String8(nameIter->first).string(), mBundle->getResourceSourceDirs()[0]);
-            for (set<String8>::iterator locales = configSet.begin();
+            for (set<String8>::const_iterator locales = configSet.begin();
                  locales != configSet.end();
                  locales++) {
                 fprintf(stdout, " %s", (*locales).string());
@@ -2496,123 +2529,6 @@ ResourceTable::validateLocalizations(void)
     return err;
 }
 
-
-status_t
-ResourceFilter::parse(const char* arg)
-{
-    if (arg == NULL) {
-        return 0;
-    }
-
-    const char* p = arg;
-    const char* q;
-
-    while (true) {
-        q = strchr(p, ',');
-        if (q == NULL) {
-            q = p + strlen(p);
-        }
-
-        String8 part(p, q-p);
-
-        if (part == "zz_ZZ") {
-            mContainsPseudo = true;
-        }
-        int axis;
-        uint32_t value;
-        if (AaptGroupEntry::parseNamePart(part, &axis, &value)) {
-            fprintf(stderr, "Invalid configuration: %s\n", arg);
-            fprintf(stderr, "                       ");
-            for (int i=0; i<p-arg; i++) {
-                fprintf(stderr, " ");
-            }
-            for (int i=0; i<q-p; i++) {
-                fprintf(stderr, "^");
-            }
-            fprintf(stderr, "\n");
-            return 1;
-        }
-
-        ssize_t index = mData.indexOfKey(axis);
-        if (index < 0) {
-            mData.add(axis, SortedVector<uint32_t>());
-        }
-        SortedVector<uint32_t>& sv = mData.editValueFor(axis);
-        sv.add(value);
-        // if it's a locale with a region, also match an unmodified locale of the
-        // same language
-        if (axis == AXIS_LANGUAGE) {
-            if (value & 0xffff0000) {
-                sv.add(value & 0x0000ffff);
-            }
-        }
-        p = q;
-        if (!*p) break;
-        p++;
-    }
-
-    return NO_ERROR;
-}
-
-bool
-ResourceFilter::match(int axis, uint32_t value)
-{
-    if (value == 0) {
-        // they didn't specify anything so take everything
-        return true;
-    }
-    ssize_t index = mData.indexOfKey(axis);
-    if (index < 0) {
-        // we didn't request anything on this axis so take everything
-        return true;
-    }
-    const SortedVector<uint32_t>& sv = mData.valueAt(index);
-    return sv.indexOf(value) >= 0;
-}
-
-bool
-ResourceFilter::match(const ResTable_config& config)
-{
-    if (config.locale) {
-        uint32_t locale = (config.country[1] << 24) | (config.country[0] << 16)
-                | (config.language[1] << 8) | (config.language[0]);
-        if (!match(AXIS_LANGUAGE, locale)) {
-            return false;
-        }
-    }
-    if (!match(AXIS_ORIENTATION, config.orientation)) {
-        return false;
-    }
-    if (!match(AXIS_UIMODETYPE, (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE))) {
-        return false;
-    }
-    if (!match(AXIS_UIMODENIGHT, (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT))) {
-        return false;
-    }
-    if (!match(AXIS_DENSITY, config.density)) {
-        return false;
-    }
-    if (!match(AXIS_TOUCHSCREEN, config.touchscreen)) {
-        return false;
-    }
-    if (!match(AXIS_KEYSHIDDEN, config.inputFlags)) {
-        return false;
-    }
-    if (!match(AXIS_KEYBOARD, config.keyboard)) {
-        return false;
-    }
-    if (!match(AXIS_NAVIGATION, config.navigation)) {
-        return false;
-    }
-    if (!match(AXIS_SCREENSIZE, config.screenSize)) {
-        return false;
-    }
-    if (!match(AXIS_VERSION, config.version)) {
-        return false;
-    }
-    return true;
-}
-
 status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
 {
     ResourceFilter filter;
@@ -2624,6 +2540,8 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
     const size_t N = mOrderedPackages.size();
     size_t pi;
 
+    const static String16 mipmap16("mipmap");
+
     bool useUTF8 = !bundle->getWantUTF16() && bundle->isMinSdkAtLeast(SDK_FROYO);
 
     // Iterate through all data, collecting all values (strings,
@@ -2646,7 +2564,10 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
                 typeStrings.add(String16("<empty>"), false);
                 continue;
             }
-            typeStrings.add(t->getName(), false);
+            const String16 typeName(t->getName());
+            typeStrings.add(typeName, false);
+
+            const bool filterable = (typeName != mipmap16);
 
             const size_t N = t->getOrderedConfigs().size();
             for (size_t ci=0; ci<N; ci++) {
@@ -2657,7 +2578,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
                 const size_t N = c->getEntries().size();
                 for (size_t ei=0; ei<N; ei++) {
                     ConfigDescription config = c->getEntries().keyAt(ei);
-                    if (!filter.match(config)) {
+                    if (filterable && !filter.match(config)) {
                         continue;
                     }
                     sp<Entry> e = c->getEntries().valueAt(ei);
@@ -2737,6 +2658,8 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
                                 "Type name %s not found",
                                 String8(typeName).string());
 
+            const bool filterable = (typeName != mipmap16);
+
             const size_t N = t != NULL ? t->getOrderedConfigs().size() : 0;
             
             // First write the typeSpec chunk, containing information about
@@ -2761,7 +2684,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
                     (((uint8_t*)data->editData())
                         + typeSpecStart + sizeof(ResTable_typeSpec));
                 memset(typeSpecFlags, 0, sizeof(uint32_t)*N);
-                        
+
                 for (size_t ei=0; ei<N; ei++) {
                     sp<ConfigList> cl = t->getOrderedConfigs().itemAt(ei);
                     if (cl->getPublic()) {
@@ -2769,11 +2692,11 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
                     }
                     const size_t CN = cl->getEntries().size();
                     for (size_t ci=0; ci<CN; ci++) {
-                        if (!filter.match(cl->getEntries().keyAt(ci))) {
+                        if (filterable && !filter.match(cl->getEntries().keyAt(ci))) {
                             continue;
                         }
                         for (size_t cj=ci+1; cj<CN; cj++) {
-                            if (!filter.match(cl->getEntries().keyAt(cj))) {
+                            if (filterable && !filter.match(cl->getEntries().keyAt(cj))) {
                                 continue;
                             }
                             typeSpecFlags[ei] |= htodl(
@@ -2793,7 +2716,8 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
                 ConfigDescription config = t->getUniqueConfigs().itemAt(ci);
 
                 NOISY(printf("Writing config %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
-                     "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n",
+                     "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
+                     "sw%ddp w%ddp h%ddp\n",
                       ti+1,
                       config.mcc, config.mnc,
                       config.language[0] ? config.language[0] : '-',
@@ -2808,9 +2732,12 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
                       config.inputFlags,
                       config.navigation,
                       config.screenWidth,
-                      config.screenHeight));
+                      config.screenHeight,
+                      config.smallestScreenWidthDp,
+                      config.screenWidthDp,
+                      config.screenHeightDp));
                       
-                if (!filter.match(config)) {
+                if (filterable && !filter.match(config)) {
                     continue;
                 }
                 
@@ -2831,7 +2758,8 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
                 tHeader->entriesStart = htodl(typeSize);
                 tHeader->config = config;
                 NOISY(printf("Writing type %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
-                     "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n",
+                     "orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
+                     "sw%ddp w%ddp h%ddp\n",
                       ti+1,
                       tHeader->config.mcc, tHeader->config.mnc,
                       tHeader->config.language[0] ? tHeader->config.language[0] : '-',
@@ -2846,7 +2774,10 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
                       tHeader->config.inputFlags,
                       tHeader->config.navigation,
                       tHeader->config.screenWidth,
-                      tHeader->config.screenHeight));
+                      tHeader->config.screenHeight,
+                      tHeader->config.smallestScreenWidthDp,
+                      tHeader->config.screenWidthDp,
+                      tHeader->config.screenHeightDp));
                 tHeader->config.swapHtoD();
 
                 // Build the entries inside of this type.
@@ -3428,7 +3359,8 @@ sp<ResourceTable::Entry> ResourceTable::Type::getEntry(const String16& entry,
     if (e == NULL) {
         if (config != NULL) {
             NOISY(printf("New entry at %s:%d: imsi:%d/%d lang:%c%c cnt:%c%c "
-                    "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n",
+                    "orien:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
+                    "sw%ddp w%ddp h%ddp\n",
                       sourcePos.file.string(), sourcePos.line,
                       config->mcc, config->mnc,
                       config->language[0] ? config->language[0] : '-',
@@ -3442,7 +3374,10 @@ sp<ResourceTable::Entry> ResourceTable::Type::getEntry(const String16& entry,
                       config->inputFlags,
                       config->navigation,
                       config->screenWidth,
-                      config->screenHeight));
+                      config->screenHeight,
+                      config->smallestScreenWidthDp,
+                      config->screenWidthDp,
+                      config->screenHeightDp));
         } else {
             NOISY(printf("New entry at %s:%d: NULL config\n",
                       sourcePos.file.string(), sourcePos.line));
index 186c7ca8fdd8d1b7d4731e76812434f52ddf3274..8123bb3d4226cc0c85f7417381ba51e69ef9508c 100644 (file)
@@ -124,6 +124,11 @@ public:
                        const String16& type,
                        const String16& name) const;
 
+    bool hasBagOrEntry(const String16& package,
+                       const String16& type,
+                       const String16& name,
+                       const ResTable_config& config) const;
+
     bool hasBagOrEntry(const String16& ref,
                        const String16* defType = NULL,
                        const String16* defPackage = NULL);
@@ -162,13 +167,13 @@ public:
     uint32_t getResId(const String16& package,
                       const String16& type,
                       const String16& name,
-                      bool onlyPublic = false) const;
+                      bool onlyPublic = true) const;
 
     uint32_t getResId(const String16& ref,
                       const String16* defType = NULL,
                       const String16* defPackage = NULL,
                       const char** outErrorMsg = NULL,
-                      bool onlyPublic = false) const;
+                      bool onlyPublic = true) const;
 
     static bool isValidResourceName(const String16& s);
     
@@ -544,19 +549,4 @@ private:
     map<String16, set<String8> > mLocalizations;
 };
 
-class ResourceFilter
-{
-public:
-    ResourceFilter() : mData(), mContainsPseudo(false) {}
-    status_t parse(const char* arg);
-    bool match(int axis, uint32_t value);
-    bool match(const ResTable_config& config);
-    inline bool containsPseudo() { return mContainsPseudo; }
-
-private:
-    KeyedVector<int,SortedVector<uint32_t> > mData;
-    bool mContainsPseudo;
-};
-
-
 #endif
index a09cec05110b4102f284225d2f0ff0526dc962c5..9a0a1c46251bf406e5fa598f79f887b0e1271e11 100644 (file)
@@ -8,6 +8,14 @@
 
 #include <utils/ByteOrder.h>
 
+#if HAVE_PRINTF_ZD
+#  define ZD "%zd"
+#  define ZD_TYPE ssize_t
+#else
+#  define ZD "%ld"
+#  define ZD_TYPE long
+#endif
+
 #define NOISY(x) //x
 
 void strcpy16_htod(uint16_t* dst, const uint16_t* src)
@@ -30,7 +38,7 @@ void printStringPool(const ResStringPool* pool)
             str = String8(pool->stringAt(s, &len)).string();
         }
 
-        printf("String #%ld: %s\n", s, str);
+        printf("String #" ZD ": %s\n", (ZD_TYPE) s, str);
     }
 }
 
index 7d14fe4b5015a185c44ab4506036ac25561d13d7..d9782b01f0e61b641bdfe702ffb1a7e43eb86bd3 100644 (file)
@@ -510,8 +510,7 @@ void printXMLBlock(ResXMLTree* block)
             namespaces.pop();
         } else if (code == ResXMLTree::TEXT) {
             size_t len;
-            printf("%sC: \"%s\"\n", prefix.string(), ResTable::normalizeForOutput(
-                        String8(block->getText(&len)).string()).string());
+            printf("%sC: \"%s\"\n", prefix.string(), String8(block->getText(&len)).string());
         }
     }
 
index a0b54c2cf78d992d2e5e17155d6d837ea57c3602..b575988f027734d97a4824bd1e1448ade612857d 100644 (file)
@@ -42,12 +42,12 @@ status_t ZipEntry::initFromCDE(FILE* fp)
     long posn;
     bool hasDD;
 
-    //LOGV("initFromCDE ---\n");
+    //ALOGV("initFromCDE ---\n");
 
     /* read the CDE */
     result = mCDE.read(fp);
     if (result != NO_ERROR) {
-        LOGD("mCDE.read failed\n");
+        ALOGD("mCDE.read failed\n");
         return result;
     }
 
@@ -56,14 +56,14 @@ status_t ZipEntry::initFromCDE(FILE* fp)
     /* using the info in the CDE, go load up the LFH */
     posn = ftell(fp);
     if (fseek(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) {
-        LOGD("local header seek failed (%ld)\n",
+        ALOGD("local header seek failed (%ld)\n",
             mCDE.mLocalHeaderRelOffset);
         return UNKNOWN_ERROR;
     }
 
     result = mLFH.read(fp);
     if (result != NO_ERROR) {
-        LOGD("mLFH.read failed\n");
+        ALOGD("mLFH.read failed\n");
         return result;
     }
 
@@ -81,7 +81,7 @@ status_t ZipEntry::initFromCDE(FILE* fp)
     hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0;
     if (hasDD) {
         // do something clever
-        //LOGD("+++ has data descriptor\n");
+        //ALOGD("+++ has data descriptor\n");
     }
 
     /*
@@ -90,7 +90,7 @@ status_t ZipEntry::initFromCDE(FILE* fp)
      * prefer the CDE values.)
      */
     if (!hasDD && !compareHeaders()) {
-        LOGW("warning: header mismatch\n");
+        ALOGW("warning: header mismatch\n");
         // keep going?
     }
 
@@ -200,7 +200,7 @@ status_t ZipEntry::addPadding(int padding)
     if (padding <= 0)
         return INVALID_OPERATION;
 
-    //LOGI("HEY: adding %d pad bytes to existing %d in %s\n",
+    //ALOGI("HEY: adding %d pad bytes to existing %d in %s\n",
     //    padding, mLFH.mExtraFieldLength, mCDE.mFileName);
 
     if (mLFH.mExtraFieldLength > 0) {
@@ -280,50 +280,50 @@ void ZipEntry::setDataInfo(long uncompLen, long compLen, unsigned long crc32,
 bool ZipEntry::compareHeaders(void) const
 {
     if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) {
-        LOGV("cmp: VersionToExtract\n");
+        ALOGV("cmp: VersionToExtract\n");
         return false;
     }
     if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) {
-        LOGV("cmp: GPBitFlag\n");
+        ALOGV("cmp: GPBitFlag\n");
         return false;
     }
     if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) {
-        LOGV("cmp: CompressionMethod\n");
+        ALOGV("cmp: CompressionMethod\n");
         return false;
     }
     if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) {
-        LOGV("cmp: LastModFileTime\n");
+        ALOGV("cmp: LastModFileTime\n");
         return false;
     }
     if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) {
-        LOGV("cmp: LastModFileDate\n");
+        ALOGV("cmp: LastModFileDate\n");
         return false;
     }
     if (mCDE.mCRC32 != mLFH.mCRC32) {
-        LOGV("cmp: CRC32\n");
+        ALOGV("cmp: CRC32\n");
         return false;
     }
     if (mCDE.mCompressedSize != mLFH.mCompressedSize) {
-        LOGV("cmp: CompressedSize\n");
+        ALOGV("cmp: CompressedSize\n");
         return false;
     }
     if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) {
-        LOGV("cmp: UncompressedSize\n");
+        ALOGV("cmp: UncompressedSize\n");
         return false;
     }
     if (mCDE.mFileNameLength != mLFH.mFileNameLength) {
-        LOGV("cmp: FileNameLength\n");
+        ALOGV("cmp: FileNameLength\n");
         return false;
     }
 #if 0       // this seems to be used for padding, not real data
     if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) {
-        LOGV("cmp: ExtraFieldLength\n");
+        ALOGV("cmp: ExtraFieldLength\n");
         return false;
     }
 #endif
     if (mCDE.mFileName != NULL) {
         if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) {
-            LOGV("cmp: FileName\n");
+            ALOGV("cmp: FileName\n");
             return false;
         }
     }
@@ -413,7 +413,7 @@ status_t ZipEntry::LocalFileHeader::read(FILE* fp)
     }
 
     if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
-        LOGD("whoops: didn't find expected signature\n");
+        ALOGD("whoops: didn't find expected signature\n");
         result = UNKNOWN_ERROR;
         goto bail;
     }
@@ -506,17 +506,17 @@ status_t ZipEntry::LocalFileHeader::write(FILE* fp)
  */
 void ZipEntry::LocalFileHeader::dump(void) const
 {
-    LOGD(" LocalFileHeader contents:\n");
-    LOGD("  versToExt=%u gpBits=0x%04x compression=%u\n",
+    ALOGD(" LocalFileHeader contents:\n");
+    ALOGD("  versToExt=%u gpBits=0x%04x compression=%u\n",
         mVersionToExtract, mGPBitFlag, mCompressionMethod);
-    LOGD("  modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
+    ALOGD("  modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
         mLastModFileTime, mLastModFileDate, mCRC32);
-    LOGD("  compressedSize=%lu uncompressedSize=%lu\n",
+    ALOGD("  compressedSize=%lu uncompressedSize=%lu\n",
         mCompressedSize, mUncompressedSize);
-    LOGD("  filenameLen=%u extraLen=%u\n",
+    ALOGD("  filenameLen=%u extraLen=%u\n",
         mFileNameLength, mExtraFieldLength);
     if (mFileName != NULL)
-        LOGD("  filename: '%s'\n", mFileName);
+        ALOGD("  filename: '%s'\n", mFileName);
 }
 
 
@@ -549,7 +549,7 @@ status_t ZipEntry::CentralDirEntry::read(FILE* fp)
     }
 
     if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
-        LOGD("Whoops: didn't find expected signature\n");
+        ALOGD("Whoops: didn't find expected signature\n");
         result = UNKNOWN_ERROR;
         goto bail;
     }
@@ -675,22 +675,22 @@ status_t ZipEntry::CentralDirEntry::write(FILE* fp)
  */
 void ZipEntry::CentralDirEntry::dump(void) const
 {
-    LOGD(" CentralDirEntry contents:\n");
-    LOGD("  versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n",
+    ALOGD(" CentralDirEntry contents:\n");
+    ALOGD("  versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n",
         mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod);
-    LOGD("  modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
+    ALOGD("  modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
         mLastModFileTime, mLastModFileDate, mCRC32);
-    LOGD("  compressedSize=%lu uncompressedSize=%lu\n",
+    ALOGD("  compressedSize=%lu uncompressedSize=%lu\n",
         mCompressedSize, mUncompressedSize);
-    LOGD("  filenameLen=%u extraLen=%u commentLen=%u\n",
+    ALOGD("  filenameLen=%u extraLen=%u commentLen=%u\n",
         mFileNameLength, mExtraFieldLength, mFileCommentLength);
-    LOGD("  diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n",
+    ALOGD("  diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n",
         mDiskNumberStart, mInternalAttrs, mExternalAttrs,
         mLocalHeaderRelOffset);
 
     if (mFileName != NULL)
-        LOGD("  filename: '%s'\n", mFileName);
+        ALOGD("  filename: '%s'\n", mFileName);
     if (mFileComment != NULL)
-        LOGD("  comment: '%s'\n", mFileComment);
+        ALOGD("  comment: '%s'\n", mFileComment);
 }
 
index 62c9383fc22a5f9c1c327c943b7f0091b6bcdc58..0705be3380dbec3585f8917f200fe41e64c5214a 100644 (file)
@@ -78,7 +78,7 @@ status_t ZipFile::open(const char* zipFileName, int flags)
         newArchive = (access(zipFileName, F_OK) != 0);
         if (!(flags & kOpenCreate) && newArchive) {
             /* not creating, must already exist */
-            LOGD("File %s does not exist", zipFileName);
+            ALOGD("File %s does not exist", zipFileName);
             return NAME_NOT_FOUND;
         }
     }
@@ -96,7 +96,7 @@ status_t ZipFile::open(const char* zipFileName, int flags)
     mZipFp = fopen(zipFileName, openflags);
     if (mZipFp == NULL) {
         int err = errno;
-        LOGD("fopen failed: %d\n", err);
+        ALOGD("fopen failed: %d\n", err);
         return errnoToStatus(err);
     }
 
@@ -215,14 +215,14 @@ status_t ZipFile::readCentralDir(void)
 
     /* too small to be a ZIP archive? */
     if (fileLength < EndOfCentralDir::kEOCDLen) {
-        LOGD("Length is %ld -- too small\n", (long)fileLength);
+        ALOGD("Length is %ld -- too small\n", (long)fileLength);
         result = INVALID_OPERATION;
         goto bail;
     }
 
     buf = new unsigned char[EndOfCentralDir::kMaxEOCDSearch];
     if (buf == NULL) {
-        LOGD("Failure allocating %d bytes for EOCD search",
+        ALOGD("Failure allocating %d bytes for EOCD search",
              EndOfCentralDir::kMaxEOCDSearch);
         result = NO_MEMORY;
         goto bail;
@@ -236,14 +236,14 @@ status_t ZipFile::readCentralDir(void)
         readAmount = (long) fileLength;
     }
     if (fseek(mZipFp, seekStart, SEEK_SET) != 0) {
-        LOGD("Failure seeking to end of zip at %ld", (long) seekStart);
+        ALOGD("Failure seeking to end of zip at %ld", (long) seekStart);
         result = UNKNOWN_ERROR;
         goto bail;
     }
 
     /* read the last part of the file into the buffer */
     if (fread(buf, 1, readAmount, mZipFp) != (size_t) readAmount) {
-        LOGD("short file? wanted %ld\n", readAmount);
+        ALOGD("short file? wanted %ld\n", readAmount);
         result = UNKNOWN_ERROR;
         goto bail;
     }
@@ -253,12 +253,12 @@ status_t ZipFile::readCentralDir(void)
         if (buf[i] == 0x50 &&
             ZipEntry::getLongLE(&buf[i]) == EndOfCentralDir::kSignature)
         {
-            LOGV("+++ Found EOCD at buf+%d\n", i);
+            ALOGV("+++ Found EOCD at buf+%d\n", i);
             break;
         }
     }
     if (i < 0) {
-        LOGD("EOCD not found, not Zip\n");
+        ALOGD("EOCD not found, not Zip\n");
         result = INVALID_OPERATION;
         goto bail;
     }
@@ -266,7 +266,7 @@ status_t ZipFile::readCentralDir(void)
     /* extract eocd values */
     result = mEOCD.readBuf(buf + i, readAmount - i);
     if (result != NO_ERROR) {
-        LOGD("Failure reading %ld bytes of EOCD values", readAmount - i);
+        ALOGD("Failure reading %ld bytes of EOCD values", readAmount - i);
         goto bail;
     }
     //mEOCD.dump();
@@ -274,7 +274,7 @@ status_t ZipFile::readCentralDir(void)
     if (mEOCD.mDiskNumber != 0 || mEOCD.mDiskWithCentralDir != 0 ||
         mEOCD.mNumEntries != mEOCD.mTotalNumEntries)
     {
-        LOGD("Archive spanning not supported\n");
+        ALOGD("Archive spanning not supported\n");
         result = INVALID_OPERATION;
         goto bail;
     }
@@ -294,7 +294,7 @@ status_t ZipFile::readCentralDir(void)
      * we're hoping to preserve.
      */
     if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
-        LOGD("Failure seeking to central dir offset %ld\n",
+        ALOGD("Failure seeking to central dir offset %ld\n",
              mEOCD.mCentralDirOffset);
         result = UNKNOWN_ERROR;
         goto bail;
@@ -303,14 +303,14 @@ status_t ZipFile::readCentralDir(void)
     /*
      * Loop through and read the central dir entries.
      */
-    LOGV("Scanning %d entries...\n", mEOCD.mTotalNumEntries);
+    ALOGV("Scanning %d entries...\n", mEOCD.mTotalNumEntries);
     int entry;
     for (entry = 0; entry < mEOCD.mTotalNumEntries; entry++) {
         ZipEntry* pEntry = new ZipEntry;
 
         result = pEntry->initFromCDE(mZipFp);
         if (result != NO_ERROR) {
-            LOGD("initFromCDE failed\n");
+            ALOGD("initFromCDE failed\n");
             delete pEntry;
             goto bail;
         }
@@ -325,16 +325,16 @@ status_t ZipFile::readCentralDir(void)
     {
         unsigned char checkBuf[4];
         if (fread(checkBuf, 1, 4, mZipFp) != 4) {
-            LOGD("EOCD check read failed\n");
+            ALOGD("EOCD check read failed\n");
             result = INVALID_OPERATION;
             goto bail;
         }
         if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) {
-            LOGD("EOCD read check failed\n");
+            ALOGD("EOCD read check failed\n");
             result = UNKNOWN_ERROR;
             goto bail;
         }
-        LOGV("+++ EOCD read check passed\n");
+        ALOGV("+++ EOCD read check passed\n");
     }
 
 bail:
@@ -416,7 +416,7 @@ status_t ZipFile::addCommon(const char* fileName, const void* data, size_t size,
             bool failed = false;
             result = compressFpToFp(mZipFp, inputFp, data, size, &crc);
             if (result != NO_ERROR) {
-                LOGD("compression failed, storing\n");
+                ALOGD("compression failed, storing\n");
                 failed = true;
             } else {
                 /*
@@ -427,7 +427,7 @@ status_t ZipFile::addCommon(const char* fileName, const void* data, size_t size,
                 long src = inputFp ? ftell(inputFp) : size;
                 long dst = ftell(mZipFp) - startPosn;
                 if (dst + (dst / 10) > src) {
-                    LOGD("insufficient compression (src=%ld dst=%ld), storing\n",
+                    ALOGD("insufficient compression (src=%ld dst=%ld), storing\n",
                         src, dst);
                     failed = true;
                 }
@@ -449,7 +449,7 @@ status_t ZipFile::addCommon(const char* fileName, const void* data, size_t size,
             }
             if (result != NO_ERROR) {
                 // don't need to truncate; happens in CDE rewrite
-                LOGD("failed copying data in\n");
+                ALOGD("failed copying data in\n");
                 goto bail;
             }
         }
@@ -468,14 +468,14 @@ status_t ZipFile::addCommon(const char* fileName, const void* data, size_t size,
         scanResult = ZipUtils::examineGzip(inputFp, &method, &uncompressedLen,
                         &compressedLen, &crc);
         if (!scanResult || method != ZipEntry::kCompressDeflated) {
-            LOGD("this isn't a deflated gzip file?");
+            ALOGD("this isn't a deflated gzip file?");
             result = UNKNOWN_ERROR;
             goto bail;
         }
 
         result = copyPartialFpToFp(mZipFp, inputFp, compressedLen, NULL);
         if (result != NO_ERROR) {
-            LOGD("failed copying gzip data in\n");
+            ALOGD("failed copying gzip data in\n");
             goto bail;
         }
     } else {
@@ -603,7 +603,7 @@ status_t ZipFile::add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,
     if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL)
         != NO_ERROR)
     {
-        LOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName);
+        ALOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName);
         result = UNKNOWN_ERROR;
         goto bail;
     }
@@ -660,7 +660,7 @@ status_t ZipFile::copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32)
         *pCRC32 = crc32(*pCRC32, tmpBuf, count);
 
         if (fwrite(tmpBuf, 1, count, dstFp) != count) {
-            LOGD("fwrite %d bytes failed\n", (int) count);
+            ALOGD("fwrite %d bytes failed\n", (int) count);
             return UNKNOWN_ERROR;
         }
     }
@@ -682,7 +682,7 @@ status_t ZipFile::copyDataToFp(FILE* dstFp,
     if (size > 0) {
         *pCRC32 = crc32(*pCRC32, (const unsigned char*)data, size);
         if (fwrite(data, 1, size, dstFp) != size) {
-            LOGD("fwrite %d bytes failed\n", (int) size);
+            ALOGD("fwrite %d bytes failed\n", (int) size);
             return UNKNOWN_ERROR;
         }
     }
@@ -716,7 +716,7 @@ status_t ZipFile::copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length,
 
         count = fread(tmpBuf, 1, readSize, srcFp);
         if ((long) count != readSize) {     // error or unexpected EOF
-            LOGD("fread %d bytes failed\n", (int) readSize);
+            ALOGD("fread %d bytes failed\n", (int) readSize);
             return UNKNOWN_ERROR;
         }
 
@@ -724,7 +724,7 @@ status_t ZipFile::copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length,
             *pCRC32 = crc32(*pCRC32, tmpBuf, count);
 
         if (fwrite(tmpBuf, 1, count, dstFp) != count) {
-            LOGD("fwrite %d bytes failed\n", (int) count);
+            ALOGD("fwrite %d bytes failed\n", (int) count);
             return UNKNOWN_ERROR;
         }
 
@@ -780,10 +780,10 @@ status_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp,
     if (zerr != Z_OK) {
         result = UNKNOWN_ERROR;
         if (zerr == Z_VERSION_ERROR) {
-            LOGE("Installed zlib is not compatible with linked version (%s)\n",
+            ALOGE("Installed zlib is not compatible with linked version (%s)\n",
                 ZLIB_VERSION);
         } else {
-            LOGD("Call to deflateInit2 failed (zerr=%d)\n", zerr);
+            ALOGD("Call to deflateInit2 failed (zerr=%d)\n", zerr);
         }
         goto bail;
     }
@@ -799,7 +799,7 @@ status_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp,
 
         /* only read if the input buffer is empty */
         if (zstream.avail_in == 0 && !atEof) {
-            LOGV("+++ reading %d bytes\n", (int)kBufSize);
+            ALOGV("+++ reading %d bytes\n", (int)kBufSize);
             if (data) {
                 getSize = size > kBufSize ? kBufSize : size;
                 memcpy(inBuf, data, getSize);
@@ -808,12 +808,12 @@ status_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp,
             } else {
                 getSize = fread(inBuf, 1, kBufSize, srcFp);
                 if (ferror(srcFp)) {
-                    LOGD("deflate read failed (errno=%d)\n", errno);
+                    ALOGD("deflate read failed (errno=%d)\n", errno);
                     goto z_bail;
                 }
             }
             if (getSize < kBufSize) {
-                LOGV("+++  got %d bytes, EOF reached\n",
+                ALOGV("+++  got %d bytes, EOF reached\n",
                     (int)getSize);
                 atEof = true;
             }
@@ -831,7 +831,7 @@ status_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp,
 
         zerr = deflate(&zstream, flush);
         if (zerr != Z_OK && zerr != Z_STREAM_END) {
-            LOGD("zlib deflate call failed (zerr=%d)\n", zerr);
+            ALOGD("zlib deflate call failed (zerr=%d)\n", zerr);
             result = UNKNOWN_ERROR;
             goto z_bail;
         }
@@ -840,11 +840,11 @@ status_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp,
         if (zstream.avail_out == 0 ||
             (zerr == Z_STREAM_END && zstream.avail_out != (uInt) kBufSize))
         {
-            LOGV("+++ writing %d bytes\n", (int) (zstream.next_out - outBuf));
+            ALOGV("+++ writing %d bytes\n", (int) (zstream.next_out - outBuf));
             if (fwrite(outBuf, 1, zstream.next_out - outBuf, dstFp) !=
                 (size_t)(zstream.next_out - outBuf))
             {
-                LOGD("write %d failed in deflate\n",
+                ALOGD("write %d failed in deflate\n",
                     (int) (zstream.next_out - outBuf));
                 goto z_bail;
             }
@@ -931,7 +931,7 @@ status_t ZipFile::flush(void)
      * of wasted space at the end of the file.  Remove it now.
      */
     if (ftruncate(fileno(mZipFp), ftell(mZipFp)) != 0) {
-        LOGW("ftruncate failed %ld: %s\n", ftell(mZipFp), strerror(errno));
+        ALOGW("ftruncate failed %ld: %s\n", ftell(mZipFp), strerror(errno));
         // not fatal
     }
 
@@ -1019,7 +1019,7 @@ status_t ZipFile::crunchArchive(void)
                         pEntry->getLFHOffset(), span);
             if (result != NO_ERROR) {
                 /* this is why you use a temp file */
-                LOGE("error during crunch - archive is toast\n");
+                ALOGE("error during crunch - archive is toast\n");
                 return result;
             }
 
@@ -1061,23 +1061,23 @@ status_t ZipFile::filemove(FILE* fp, off_t dst, off_t src, size_t n)
                 getSize = n;
 
             if (fseek(fp, (long) src, SEEK_SET) != 0) {
-                LOGD("filemove src seek %ld failed\n", (long) src);
+                ALOGD("filemove src seek %ld failed\n", (long) src);
                 return UNKNOWN_ERROR;
             }
 
             if (fread(readBuf, 1, getSize, fp) != getSize) {
-                LOGD("filemove read %ld off=%ld failed\n",
+                ALOGD("filemove read %ld off=%ld failed\n",
                     (long) getSize, (long) src);
                 return UNKNOWN_ERROR;
             }
 
             if (fseek(fp, (long) dst, SEEK_SET) != 0) {
-                LOGD("filemove dst seek %ld failed\n", (long) dst);
+                ALOGD("filemove dst seek %ld failed\n", (long) dst);
                 return UNKNOWN_ERROR;
             }
 
             if (fwrite(readBuf, 1, getSize, fp) != getSize) {
-                LOGD("filemove write %ld off=%ld failed\n",
+                ALOGD("filemove write %ld off=%ld failed\n",
                     (long) getSize, (long) dst);
                 return UNKNOWN_ERROR;
             }
@@ -1104,7 +1104,7 @@ time_t ZipFile::getModTime(int fd)
     struct stat sb;
 
     if (fstat(fd, &sb) < 0) {
-        LOGD("HEY: fstat on fd %d failed\n", fd);
+        ALOGD("HEY: fstat on fd %d failed\n", fd);
         return (time_t) -1;
     }
 
@@ -1129,7 +1129,7 @@ int ZipFile::getZipFd(void) const
     int fd;
     fd = dup(fileno(mZipFp));
     if (fd < 0) {
-        LOGD("didn't work, errno=%d\n", errno);
+        ALOGD("didn't work, errno=%d\n", errno);
     }
 
     return fd;
@@ -1224,7 +1224,7 @@ status_t ZipFile::EndOfCentralDir::readBuf(const unsigned char* buf, int len)
 
     if (len < kEOCDLen) {
         /* looks like ZIP file got truncated */
-        LOGD(" Zip EOCD: expected >= %d bytes, found %d\n",
+        ALOGD(" Zip EOCD: expected >= %d bytes, found %d\n",
             kEOCDLen, len);
         return INVALID_OPERATION;
     }
@@ -1245,7 +1245,7 @@ status_t ZipFile::EndOfCentralDir::readBuf(const unsigned char* buf, int len)
 
     if (mCommentLen > 0) {
         if (kEOCDLen + mCommentLen > len) {
-            LOGD("EOCD(%d) + comment(%d) exceeds len (%d)\n",
+            ALOGD("EOCD(%d) + comment(%d) exceeds len (%d)\n",
                 kEOCDLen, mCommentLen, len);
             return UNKNOWN_ERROR;
         }
@@ -1288,10 +1288,10 @@ status_t ZipFile::EndOfCentralDir::write(FILE* fp)
  */
 void ZipFile::EndOfCentralDir::dump(void) const
 {
-    LOGD(" EndOfCentralDir contents:\n");
-    LOGD("  diskNum=%u diskWCD=%u numEnt=%u totalNumEnt=%u\n",
+    ALOGD(" EndOfCentralDir contents:\n");
+    ALOGD("  diskNum=%u diskWCD=%u numEnt=%u totalNumEnt=%u\n",
         mDiskNumber, mDiskWithCentralDir, mNumEntries, mTotalNumEntries);
-    LOGD("  centDirSize=%lu centDirOff=%lu commentLen=%u\n",
+    ALOGD("  centDirSize=%lu centDirOff=%lu commentLen=%u\n",
         mCentralDirSize, mCentralDirOffset, mCommentLen);
 }
 
index dbbd072d1692decfe2b79e855d5026f95ca2c8a2..78775502884bba157c7be86b88f6c3d9fd7e561f 100644 (file)
--- a/ZipFile.h
+++ b/ZipFile.h
@@ -57,7 +57,7 @@ public:
     /*
      * Open a new or existing archive.
      */
-    typedef enum {
+    enum {
         kOpenReadOnly   = 0x01,
         kOpenReadWrite  = 0x02,
         kOpenCreate     = 0x04,     // create if it doesn't exist
diff --git a/tests/CrunchCache_test.cpp b/tests/CrunchCache_test.cpp
new file mode 100644 (file)
index 0000000..20b5022
--- /dev/null
@@ -0,0 +1,97 @@
+//
+// Copyright 2011 The Android Open Source Project
+//
+#include <utils/String8.h>
+#include <iostream>
+#include <errno.h>
+
+#include "CrunchCache.h"
+#include "FileFinder.h"
+#include "MockFileFinder.h"
+#include "CacheUpdater.h"
+#include "MockCacheUpdater.h"
+
+using namespace android;
+using std::cout;
+using std::endl;
+
+void expectEqual(int got, int expected, const char* desc) {
+    cout << "Checking " << desc << ": ";
+    cout << "Got " << got << ", expected " << expected << "...";
+    cout << ( (got == expected) ? "PASSED" : "FAILED") << endl;
+    errno += ((got == expected) ? 0 : 1);
+}
+
+int main() {
+
+    errno = 0;
+
+    String8 source("res");
+    String8 dest("res2");
+
+    // Create data for MockFileFinder to feed to the cache
+    KeyedVector<String8, time_t> sourceData;
+    // This shouldn't be updated
+    sourceData.add(String8("res/drawable/hello.png"),3);
+    // This should be updated
+    sourceData.add(String8("res/drawable/world.png"),5);
+    // This should cause make directory to be called
+    sourceData.add(String8("res/drawable-cool/hello.png"),3);
+
+    KeyedVector<String8, time_t> destData;
+    destData.add(String8("res2/drawable/hello.png"),3);
+    destData.add(String8("res2/drawable/world.png"),3);
+    // this should call delete
+    destData.add(String8("res2/drawable/dead.png"),3);
+
+    // Package up data and create mock file finder
+    KeyedVector<String8, KeyedVector<String8,time_t> > data;
+    data.add(source,sourceData);
+    data.add(dest,destData);
+    FileFinder* ff = new MockFileFinder(data);
+    CrunchCache cc(source,dest,ff);
+
+    MockCacheUpdater* mcu = new MockCacheUpdater();
+    CacheUpdater* cu(mcu);
+
+    cout << "Running Crunch...";
+    int result = cc.crunch(cu);
+    cout << ((result > 0) ? "PASSED" : "FAILED") << endl;
+    errno += ((result > 0) ? 0 : 1);
+
+    const int EXPECTED_RESULT = 2;
+    expectEqual(result, EXPECTED_RESULT, "number of files touched");
+
+    cout << "Checking calls to deleteFile and processImage:" << endl;
+    const int EXPECTED_DELETES = 1;
+    const int EXPECTED_PROCESSED = 2;
+    // Deletes
+    expectEqual(mcu->deleteCount, EXPECTED_DELETES, "deleteFile");
+    // processImage
+    expectEqual(mcu->processCount, EXPECTED_PROCESSED, "processImage");
+
+    const int EXPECTED_OVERWRITES = 3;
+    result = cc.crunch(cu, true);
+    expectEqual(result, EXPECTED_OVERWRITES, "number of files touched with overwrite");
+    \
+
+    if (errno == 0)
+        cout << "ALL TESTS PASSED!" << endl;
+    else
+        cout << errno << " TESTS FAILED" << endl;
+
+    delete ff;
+    delete cu;
+
+    // TESTS BELOW WILL GO AWAY SOON
+
+    String8 source2("ApiDemos/res");
+    String8 dest2("ApiDemos/res2");
+
+    FileFinder* sff = new SystemFileFinder();
+    CacheUpdater* scu = new SystemCacheUpdater();
+
+    CrunchCache scc(source2,dest2,sff);
+
+    scc.crunch(scu);
+}
\ No newline at end of file
diff --git a/tests/FileFinder_test.cpp b/tests/FileFinder_test.cpp
new file mode 100644 (file)
index 0000000..07bd665
--- /dev/null
@@ -0,0 +1,101 @@
+//
+// Copyright 2011 The Android Open Source Project
+//
+#include <utils/Vector.h>
+#include <utils/KeyedVector.h>
+#include <iostream>
+#include <cassert>
+#include <utils/String8.h>
+#include <utility>
+
+#include "DirectoryWalker.h"
+#include "MockDirectoryWalker.h"
+#include "FileFinder.h"
+
+using namespace android;
+
+using std::pair;
+using std::cout;
+using std::endl;
+
+
+
+int main()
+{
+
+    cout << "\n\n STARTING FILE FINDER TESTS" << endl;
+    String8 path("ApiDemos");
+
+    // Storage to pass to findFiles()
+    KeyedVector<String8,time_t> testStorage;
+
+    // Mock Directory Walker initialization. First data, then sdw
+    Vector< pair<String8,time_t> > data;
+    data.push( pair<String8,time_t>(String8("hello.png"),3) );
+    data.push( pair<String8,time_t>(String8("world.PNG"),3) );
+    data.push( pair<String8,time_t>(String8("foo.pNg"),3) );
+    // Neither of these should be found
+    data.push( pair<String8,time_t>(String8("hello.jpg"),3) );
+    data.push( pair<String8,time_t>(String8(".hidden.png"),3));
+
+    DirectoryWalker* sdw = new StringDirectoryWalker(path,data);
+
+    // Extensions to look for
+    Vector<String8> exts;
+    exts.push(String8(".png"));
+
+    errno = 0;
+
+    // Make sure we get a valid mock directory walker
+    // Make sure we finish without errors
+    cout << "Checking DirectoryWalker...";
+    assert(sdw != NULL);
+    cout << "PASSED" << endl;
+
+    // Make sure we finish without errors
+    cout << "Running findFiles()...";
+    bool findStatus = FileFinder::findFiles(path,exts, testStorage, sdw);
+    assert(findStatus);
+    cout << "PASSED" << endl;
+
+    const size_t SIZE_EXPECTED = 3;
+    // Check to make sure we have the right number of things in our storage
+    cout << "Running size comparison: Size is " << testStorage.size() << ", ";
+    cout << "Expected " << SIZE_EXPECTED << "...";
+    if(testStorage.size() == SIZE_EXPECTED)
+        cout << "PASSED" << endl;
+    else {
+        cout << "FAILED" << endl;
+        errno++;
+    }
+
+    // Check to make sure that each of our found items has the right extension
+    cout << "Checking Returned Extensions...";
+    bool extsOkay = true;
+    String8 wrongExts;
+    for (size_t i = 0; i < SIZE_EXPECTED; ++i) {
+        String8 testExt(testStorage.keyAt(i).getPathExtension());
+        testExt.toLower();
+        if (testExt != ".png") {
+            wrongExts += testStorage.keyAt(i);
+            wrongExts += "\n";
+            extsOkay = false;
+        }
+    }
+    if (extsOkay)
+        cout << "PASSED" << endl;
+    else {
+        cout << "FAILED" << endl;
+        cout << "The following extensions didn't check out" << endl << wrongExts;
+    }
+
+    // Clean up
+    delete sdw;
+
+    if(errno == 0) {
+        cout << "ALL TESTS PASSED" << endl;
+    } else {
+        cout << errno << " TESTS FAILED" << endl;
+    }
+    return errno;
+}
\ No newline at end of file
diff --git a/tests/MockCacheUpdater.h b/tests/MockCacheUpdater.h
new file mode 100644 (file)
index 0000000..c7f4bd7
--- /dev/null
@@ -0,0 +1,40 @@
+//
+// Copyright 2011 The Android Open Source Project
+//
+#ifndef MOCKCACHEUPDATER_H
+#define MOCKCACHEUPDATER_H
+
+#include <utils/String8.h>
+#include "CacheUpdater.h"
+
+using namespace android;
+
+class MockCacheUpdater : public CacheUpdater {
+public:
+
+    MockCacheUpdater()
+        : deleteCount(0), processCount(0) { };
+
+    // Make sure all the directories along this path exist
+    virtual void ensureDirectoriesExist(String8 path)
+    {
+        // Nothing to do
+    };
+
+    // Delete a file
+    virtual void deleteFile(String8 path) {
+        deleteCount++;
+    };
+
+    // Process an image from source out to dest
+    virtual void processImage(String8 source, String8 dest) {
+        processCount++;
+    };
+
+    // DATA MEMBERS
+    int deleteCount;
+    int processCount;
+private:
+};
+
+#endif // MOCKCACHEUPDATER_H
\ No newline at end of file
diff --git a/tests/MockDirectoryWalker.h b/tests/MockDirectoryWalker.h
new file mode 100644 (file)
index 0000000..5900cf3
--- /dev/null
@@ -0,0 +1,85 @@
+//
+// Copyright 2011 The Android Open Source Project
+//
+#ifndef MOCKDIRECTORYWALKER_H
+#define MOCKDIRECTORYWALKER_H
+
+#include <utils/Vector.h>
+#include <utils/String8.h>
+#include <utility>
+#include "DirectoryWalker.h"
+
+using namespace android;
+using std::pair;
+
+// String8 Directory Walker
+// This is an implementation of the Directory Walker abstraction that is built
+// for testing.
+// Instead of system calls it queries a private data structure for the directory
+// entries. It takes a path and a map of filenames and their modification times.
+// functions are inlined since they are short and simple
+
+class StringDirectoryWalker : public DirectoryWalker {
+public:
+    StringDirectoryWalker(String8& path, Vector< pair<String8,time_t> >& data)
+        :  mPos(0), mBasePath(path), mData(data) {
+        //fprintf(stdout,"StringDW built to mimic %s with %d files\n",
+        //       mBasePath.string());
+    };
+    // Default copy constructor, and destructor are fine
+
+    virtual bool openDir(String8 path) {
+        // If the user is trying to query the "directory" that this
+        // walker was initialized with, then return success. Else fail.
+        return path == mBasePath;
+    };
+    virtual bool openDir(const char* path) {
+        String8 p(path);
+        openDir(p);
+        return true;
+    };
+    // Advance to next entry in the Vector
+    virtual struct dirent* nextEntry() {
+        // Advance position and check to see if we're done
+        if (mPos >= mData.size())
+            return NULL;
+
+        // Place data in the entry descriptor. This class only returns files.
+        mEntry.d_type = DT_REG;
+        mEntry.d_ino = mPos;
+        // Copy chars from the string name to the entry name
+        size_t i = 0;
+        for (i; i < mData[mPos].first.size(); ++i)
+            mEntry.d_name[i] = mData[mPos].first[i];
+        mEntry.d_name[i] = '\0';
+
+        // Place data in stats
+        mStats.st_ino = mPos;
+        mStats.st_mtime = mData[mPos].second;
+
+        // Get ready to move to the next entry
+        mPos++;
+
+        return &mEntry;
+    };
+    // Get the stats for the current entry
+    virtual struct stat*   entryStats() {
+        return &mStats;
+    };
+    // Nothing to do in clean up
+    virtual void closeDir() {
+        // Nothing to do
+    };
+    virtual DirectoryWalker* clone() {
+        return new StringDirectoryWalker(*this);
+    };
+private:
+    // Current position in the Vector
+    size_t mPos;
+    // Base path
+    String8 mBasePath;
+    // Data to simulate a directory full of files.
+    Vector< pair<String8,time_t> > mData;
+};
+
+#endif // MOCKDIRECTORYWALKER_H
\ No newline at end of file
diff --git a/tests/MockFileFinder.h b/tests/MockFileFinder.h
new file mode 100644 (file)
index 0000000..da5ea4f
--- /dev/null
@@ -0,0 +1,55 @@
+//
+// Copyright 2011 The Android Open Source Project
+//
+
+#ifndef MOCKFILEFINDER_H
+#define MOCKFILEFINDER_H
+
+#include <utils/Vector.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+
+#include "DirectoryWalker.h"
+
+using namespace android;
+
+class MockFileFinder : public FileFinder {
+public:
+    MockFileFinder (KeyedVector<String8, KeyedVector<String8,time_t> >& files)
+        : mFiles(files)
+    {
+        // Nothing left to do
+    };
+
+    /**
+     * findFiles implementation for the abstraction.
+     * PRECONDITIONS:
+     *  No checking is done, so there MUST be an entry in mFiles with
+     *  path matching basePath.
+     *
+     * POSTCONDITIONS:
+     *  fileStore is filled with a copy of the data in mFiles corresponding
+     *  to the basePath.
+     */
+
+    virtual bool findFiles(String8 basePath, Vector<String8>& extensions,
+                           KeyedVector<String8,time_t>& fileStore,
+                           DirectoryWalker* dw)
+    {
+        const KeyedVector<String8,time_t>* payload(&mFiles.valueFor(basePath));
+        // Since KeyedVector doesn't implement swap
+        // (who doesn't use swap??) we loop and add one at a time.
+        for (size_t i = 0; i < payload->size(); ++i) {
+            fileStore.add(payload->keyAt(i),payload->valueAt(i));
+        }
+        return true;
+    }
+
+private:
+    // Virtual mapping between "directories" and the "files" contained
+    // in them
+    KeyedVector<String8, KeyedVector<String8,time_t> > mFiles;
+};
+
+
+#endif // MOCKFILEFINDER_H
\ No newline at end of file