]> git.saurik.com Git - android/aapt.git/blobdiff - AaptAssets.cpp
Normalize output from aapt d xmltree
[android/aapt.git] / AaptAssets.cpp
index dbcef6d67480afbccc7127db79f8286ebf2e25fe..22f495ae7a38eb3ac9256209b5412c7b586d112f 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;
 
@@ -53,51 +56,93 @@ static bool validateFileName(const char* fileName)
     return true;
 }
 
+// The default to use if no other ignore pattern is defined.
+const char * const gDefaultIgnoreAssets =
+    "!.svn:!.git:!.ds_store:!*.scc:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~";
+// The ignore pattern that can be passed via --ignore-assets in Main.cpp
+const char * gUserIgnoreAssets = NULL;
+
 static bool isHidden(const char *root, const char *path)
 {
-    const char *type = NULL;
-
-    // Skip all hidden files.
-    if (path[0] == '.') {
-        // Skip ., .. and  .svn but don't chatter about it.
-        if (strcmp(path, ".") == 0
-            || strcmp(path, "..") == 0
-            || strcmp(path, ".svn") == 0) {
-            return true;
-        }
-        type = "hidden";
-    } else if (path[0] == '_') {
-        // skip directories starting with _ (don't chatter about it)
-        String8 subdirName(root);
-        subdirName.appendPath(path);
-        if (getFileType(subdirName.string()) == kFileTypeDirectory) {
-            return true;
-        }
-    } else if (strcmp(path, "CVS") == 0) {
-        // Skip CVS but don't chatter about it.
+    // Patterns syntax:
+    // - Delimiter is :
+    // - Entry can start with the flag ! to avoid printing a warning
+    //   about the file being ignored.
+    // - Entry can have the flag "<dir>" to match only directories
+    //   or <file> to match only files. Default is to match both.
+    // - Entry can be a simplified glob "<prefix>*" or "*<suffix>"
+    //   where prefix/suffix must have at least 1 character (so that
+    //   we don't match a '*' catch-all pattern.)
+    // - The special filenames "." and ".." are always ignored.
+    // - Otherwise the full string is matched.
+    // - match is not case-sensitive.
+
+    if (strcmp(path, ".") == 0 || strcmp(path, "..") == 0) {
         return true;
-    } else if (strcasecmp(path, "thumbs.db") == 0
-               || strcasecmp(path, "picasa.ini") == 0) {
-        // Skip suspected image indexes files.
-        type = "index";
-    } else if (path[strlen(path)-1] == '~') {
-        // Skip suspected emacs backup files.
-        type = "backup";
-    } else {
-        // Let everything else through.
-        return false;
     }
 
-    /* If we get this far, "type" should be set and the file
-     * should be skipped.
-     */
-    String8 subdirName(root);
-    subdirName.appendPath(path);
-    fprintf(stderr, "    (skipping %s %s '%s')\n", type,
-            getFileType(subdirName.string())==kFileTypeDirectory ? "dir":"file",
-            subdirName.string());
+    const char *delim = ":";
+    const char *p = gUserIgnoreAssets;
+    if (!p || !p[0]) {
+        p = getenv("ANDROID_AAPT_IGNORE");
+    }
+    if (!p || !p[0]) {
+        p = gDefaultIgnoreAssets;
+    }
+    char *patterns = strdup(p);
 
-    return true;
+    bool ignore = false;
+    bool chatty = true;
+    char *matchedPattern = NULL;
+
+    String8 fullPath(root);
+    fullPath.appendPath(path);
+    FileType type = getFileType(fullPath);
+
+    int plen = strlen(path);
+
+    // Note: we don't have strtok_r under mingw.
+    for(char *token = strtok(patterns, delim);
+            !ignore && token != NULL;
+            token = strtok(NULL, delim)) {
+        chatty = token[0] != '!';
+        if (!chatty) token++; // skip !
+        if (strncasecmp(token, "<dir>" , 5) == 0) {
+            if (type != kFileTypeDirectory) continue;
+            token += 5;
+        }
+        if (strncasecmp(token, "<file>", 6) == 0) {
+            if (type != kFileTypeRegular) continue;
+            token += 6;
+        }
+
+        matchedPattern = token;
+        int n = strlen(token);
+
+        if (token[0] == '*') {
+            // Match *suffix
+            token++;
+            n--;
+            if (n <= plen) {
+                ignore = strncasecmp(token, path + plen - n, n) == 0;
+            }
+        } else if (n > 1 && token[n - 1] == '*') {
+            // Match prefix*
+            ignore = strncasecmp(token, path, n - 1) == 0;
+        } else {
+            ignore = strcasecmp(token, path) == 0;
+        }
+    }
+
+    if (ignore && chatty) {
+        fprintf(stderr, "    (skipping %s '%s' due to ANDROID_AAPT_IGNORE pattern '%s')\n",
+                type == kFileTypeDirectory ? "dir" : "file",
+                path,
+                matchedPattern ? matchedPattern : "");
+    }
+
+    free(patterns);
+    return ignore;
 }
 
 // =========================================================================
@@ -138,6 +183,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;
@@ -159,6 +225,20 @@ AaptGroupEntry::parseNamePart(const String8& part, int* axis, uint32_t* value)
         return 0;
     }
 
+    // ui mode type
+    if (getUiModeTypeName(part.string(), &config)) {
+        *axis = AXIS_UIMODETYPE;
+        *value = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
+        return 0;
+    }
+
+    // ui mode night
+    if (getUiModeNightName(part.string(), &config)) {
+        *axis = AXIS_UIMODENIGHT;
+        *value = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
+        return 0;
+    }
+
     // density
     if (getDensityName(part.string(), &config)) {
         *axis = AXIS_DENSITY;
@@ -187,6 +267,13 @@ AaptGroupEntry::parseNamePart(const String8& part, int* axis, uint32_t* value)
         return 0;
     }
 
+    // navigation hidden
+    if (getNavHiddenName(part.string(), &config)) {
+        *axis = AXIS_NAVHIDDEN;
+        *value = config.inputFlags;
+        return 0;
+    }
+
     // navigation
     if (getNavigationName(part.string(), &config)) {
         *axis = AXIS_NAVIGATION;
@@ -211,13 +298,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, size, vers;
+    String8 touch, key, keysHidden, nav, navHidden, size, vers;
+    String8 uiModeType, uiModeNight, smallestwidthdp, widthdp, heightdp;
 
     const char *p = dir;
     const char *q;
@@ -304,6 +452,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;
 
@@ -341,6 +525,32 @@ AaptGroupEntry::initFromDirName(const char* dir, String8* resType)
         //printf("not orientation: %s\n", part.string());
     }
 
+    // ui mode type
+    if (getUiModeTypeName(part.string())) {
+        uiModeType = part;
+
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index];
+    } else {
+        //printf("not ui mode type: %s\n", part.string());
+    }
+
+    // ui mode night
+    if (getUiModeNightName(part.string())) {
+        uiModeNight = part;
+
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index];
+    } else {
+        //printf("not ui mode night: %s\n", part.string());
+    }
+
     // density
     if (getDensityName(part.string())) {
         den = part;
@@ -393,6 +603,19 @@ AaptGroupEntry::initFromDirName(const char* dir, String8* resType)
         //printf("not keyboard: %s\n", part.string());
     }
 
+    // navigation hidden
+    if (getNavHiddenName(part.string())) {
+        navHidden = part;
+
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index];
+    } else {
+        //printf("not navHidden: %s\n", part.string());
+    }
+
     if (getNavigationName(part.string())) {
         nav = part;
 
@@ -438,11 +661,17 @@ 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;
     this->density = den;
     this->touchscreen = touch;
     this->keysHidden = keysHidden;
     this->keyboard = key;
+    this->navHidden = navHidden;
     this->navigation = nav;
     this->screenSize = size;
     this->version = vers;
@@ -462,12 +691,22 @@ AaptGroupEntry::toString() const
     s += ",";
     s += this->locale;
     s += ",";
+    s += smallestScreenWidthDp;
+    s += ",";
+    s += screenWidthDp;
+    s += ",";
+    s += screenHeightDp;
+    s += ",";
     s += screenLayoutSize;
     s += ",";
     s += screenLayoutLong;
     s += ",";
     s += this->orientation;
     s += ",";
+    s += uiModeType;
+    s += ",";
+    s += uiModeNight;
+    s += ",";
     s += density;
     s += ",";
     s += touchscreen;
@@ -476,6 +715,8 @@ AaptGroupEntry::toString() const
     s += ",";
     s += keyboard;
     s += ",";
+    s += navHidden;
+    s += ",";
     s += navigation;
     s += ",";
     s += screenSize;
@@ -489,55 +730,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 != "") {
+        if (s.length() > 0) {
+            s += "-";
+        }
+        s += uiModeType;
+    }
+    if (this->uiModeNight != "") {
+        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 != "") {
+        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;
     }
 
@@ -599,13 +902,11 @@ bool AaptGroupEntry::getMncName(const char* name,
     if (*c != 0) return false;
     if (c-val == 0 || c-val > 3) return false;
 
-    int d = atoi(val);
-    if (d != 0) {
-        if (out) out->mnc = d;
-        return true;
+    if (out) {
+        out->mnc = atoi(val);
     }
 
-    return false;
+    return true;
 }
 
 /*
@@ -680,6 +981,11 @@ bool AaptGroupEntry::getScreenLayoutSizeName(const char* name,
                 (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
                 | ResTable_config::SCREENSIZE_LARGE;
         return true;
+    } else if (strcmp(name, "xlarge") == 0) {
+        if (out) out->screenLayout =
+                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
+                | ResTable_config::SCREENSIZE_XLARGE;
+        return true;
     }
 
     return false;
@@ -728,6 +1034,57 @@ bool AaptGroupEntry::getOrientationName(const char* name,
     return false;
 }
 
+bool AaptGroupEntry::getUiModeTypeName(const char* name,
+                                       ResTable_config* out)
+{
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->uiMode =
+                (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+                | ResTable_config::UI_MODE_TYPE_ANY;
+        return true;
+    } else if (strcmp(name, "desk") == 0) {
+      if (out) out->uiMode =
+              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+              | ResTable_config::UI_MODE_TYPE_DESK;
+        return true;
+    } else if (strcmp(name, "car") == 0) {
+      if (out) out->uiMode =
+              (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;
+}
+
+bool AaptGroupEntry::getUiModeNightName(const char* name,
+                                          ResTable_config* out)
+{
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->uiMode =
+                (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
+                | ResTable_config::UI_MODE_NIGHT_ANY;
+        return true;
+    } else if (strcmp(name, "night") == 0) {
+        if (out) out->uiMode =
+                (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
+                | ResTable_config::UI_MODE_NIGHT_YES;
+        return true;
+    } else if (strcmp(name, "notnight") == 0) {
+      if (out) out->uiMode =
+              (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
+              | ResTable_config::UI_MODE_NIGHT_NO;
+        return true;
+    }
+
+    return false;
+}
+
 bool AaptGroupEntry::getDensityName(const char* name,
                                     ResTable_config* out)
 {
@@ -751,11 +1108,21 @@ 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;
     }
     
+    if (strcmp(name, "xhdpi") == 0) {
+        if (out) out->density = ResTable_config::DENSITY_MEDIUM*2;
+        return true;
+    }
+    
     char* c = (char*)name;
     while (*c >= '0' && *c <= '9') {
         c++;
@@ -811,17 +1178,17 @@ bool AaptGroupEntry::getKeysHiddenName(const char* name,
     uint8_t mask = 0;
     uint8_t value = 0;
     if (strcmp(name, kWildcardName) == 0) {
-        mask = out->MASK_KEYSHIDDEN;
-        value = out->KEYSHIDDEN_ANY;
+        mask = ResTable_config::MASK_KEYSHIDDEN;
+        value = ResTable_config::KEYSHIDDEN_ANY;
     } else if (strcmp(name, "keysexposed") == 0) {
-        mask = out->MASK_KEYSHIDDEN;
-        value = out->KEYSHIDDEN_NO;
+        mask = ResTable_config::MASK_KEYSHIDDEN;
+        value = ResTable_config::KEYSHIDDEN_NO;
     } else if (strcmp(name, "keyshidden") == 0) {
-        mask = out->MASK_KEYSHIDDEN;
-        value = out->KEYSHIDDEN_YES;
+        mask = ResTable_config::MASK_KEYSHIDDEN;
+        value = ResTable_config::KEYSHIDDEN_YES;
     } else if (strcmp(name, "keyssoft") == 0) {
-        mask = out->MASK_KEYSHIDDEN;
-        value = out->KEYSHIDDEN_SOFT;
+        mask = ResTable_config::MASK_KEYSHIDDEN;
+        value = ResTable_config::KEYSHIDDEN_SOFT;
     }
 
     if (mask != 0) {
@@ -852,6 +1219,30 @@ bool AaptGroupEntry::getKeyboardName(const char* name,
     return false;
 }
 
+bool AaptGroupEntry::getNavHiddenName(const char* name,
+                                       ResTable_config* out)
+{
+    uint8_t mask = 0;
+    uint8_t value = 0;
+    if (strcmp(name, kWildcardName) == 0) {
+        mask = ResTable_config::MASK_NAVHIDDEN;
+        value = ResTable_config::NAVHIDDEN_ANY;
+    } else if (strcmp(name, "navexposed") == 0) {
+        mask = ResTable_config::MASK_NAVHIDDEN;
+        value = ResTable_config::NAVHIDDEN_NO;
+    } else if (strcmp(name, "navhidden") == 0) {
+        mask = ResTable_config::MASK_NAVHIDDEN;
+        value = ResTable_config::NAVHIDDEN_YES;
+    }
+
+    if (mask != 0) {
+        if (out) out->inputFlags = (out->inputFlags&~mask) | value;
+        return true;
+    }
+
+    return false;
+}
+
 bool AaptGroupEntry::getNavigationName(const char* name,
                                      ResTable_config* out)
 {
@@ -875,8 +1266,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) {
@@ -911,8 +1301,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) {
@@ -946,36 +1406,77 @@ 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);
+    if (v == 0) v = uiModeType.compare(o.uiModeType);
+    if (v == 0) v = uiModeNight.compare(o.uiModeNight);
     if (v == 0) v = density.compare(o.density);
     if (v == 0) v = touchscreen.compare(o.touchscreen);
     if (v == 0) v = keysHidden.compare(o.keysHidden);
     if (v == 0) v = keyboard.compare(o.keyboard);
+    if (v == 0) v = navHidden.compare(o.navHidden);
     if (v == 0) v = navigation.compare(o.navigation);
     if (v == 0) v = screenSize.compare(o.screenSize);
     if (v == 0) v = version.compare(o.version);
     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);
+    getUiModeTypeName(uiModeType.string(), &params);
+    getUiModeNightName(uiModeNight.string(), &params);
     getDensityName(density.string(), &params);
     getTouchscreenName(touchscreen.string(), &params);
     getKeysHiddenName(keysHidden.string(), &params);
     getKeyboardName(keyboard.string(), &params);
+    getNavHiddenName(navHidden.string(), &params);
     getNavigationName(navigation.string(), &params);
     getScreenSizeName(screenSize.string(), &params);
     getVersionName(version.string(), &params);
+    
+    // Fix up version number based on specified parameters.
+    int minSdk = 0;
+    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) {
+        minSdk = SDK_FROYO;
+    } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE)
+                != ResTable_config::SCREENSIZE_ANY
+            ||  (params.screenLayout&ResTable_config::MASK_SCREENLONG)
+                != ResTable_config::SCREENLONG_ANY
+            || params.density != ResTable_config::DENSITY_DEFAULT) {
+        minSdk = SDK_DONUT;
+    }
+    
+    if (minSdk > params.sdkVersion) {
+        params.sdkVersion = minSdk;
+    }
+    
     return params;
 }
 
@@ -1046,8 +1547,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;
@@ -1067,6 +1567,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;
@@ -1077,20 +1584,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());
     }
 }
 
@@ -1157,38 +1667,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;
@@ -1203,10 +1681,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;
 
@@ -1229,9 +1707,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);
     }
 
@@ -1258,7 +1741,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;
             }
@@ -1348,17 +1831,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);
     }
 }
 
@@ -1382,6 +1865,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,
@@ -1490,7 +1991,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;
@@ -1521,6 +2022,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));
 
@@ -1563,7 +2065,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;
@@ -1582,6 +2084,11 @@ ssize_t AaptAssets::slurpFromArgs(Bundle* bundle)
         goto bail;
     }
 
+    count = filter(bundle);
+    if (count != NO_ERROR) {
+        totalCount = count;
+        goto bail;
+    }
 
 bail:
     return totalCount;
@@ -1589,9 +2096,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);
     }
@@ -1638,12 +2146,22 @@ ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
             continue;
         }
 
+        if (bundle->getMaxResVersion() != NULL && group.getVersionString().length() != 0) {
+            int maxResInt = atoi(bundle->getMaxResVersion());
+            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);
+              continue;
+            }
+        }
+
         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;
@@ -1653,7 +2171,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());
@@ -1762,6 +2286,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);
@@ -1805,18 +2465,44 @@ 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("\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);
     }
+}
 
-    printf("\nFiles:\n");
-    AaptDir::print();
+sp<AaptDir> AaptAssets::resDir(const String8& name) const
+{
+    const Vector<sp<AaptDir> >& resdirs = mResDirs;
+    const size_t N = resdirs.size();
+    for (size_t i=0; i<N; i++) {
+        const sp<AaptDir>& d = resdirs.itemAt(i);
+        if (d->getLeaf() == name) {
+            return d;
+        }
+    }
+    return NULL;
 }
 
 bool