diff options
Diffstat (limited to 'tools')
73 files changed, 3418 insertions, 2567 deletions
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp index 1f17316..12d5389 100644 --- a/tools/aapt/AaptAssets.cpp +++ b/tools/aapt/AaptAssets.cpp @@ -3,8 +3,10 @@ // #include "AaptAssets.h" -#include "ResourceFilter.h" +#include "AaptConfig.h" +#include "AaptUtil.h" #include "Main.h" +#include "ResourceFilter.h" #include <utils/misc.h> #include <utils/SortedVector.h> @@ -14,7 +16,6 @@ #include <errno.h> 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"; @@ -149,24 +150,6 @@ static bool isHidden(const char *root, const char *path) // ========================================================================= // ========================================================================= -/* static */ void AaptLocaleValue::splitAndLowerCase(const char* const chars, - Vector<String8>* parts, const char separator) { - const char *p = chars; - const char *q; - while (NULL != (q = strchr(p, separator))) { - String8 val(p, q - p); - val.toLower(); - parts->add(val); - p = q+1; - } - - if (p < chars + strlen(chars)) { - String8 val(p); - val.toLower(); - parts->add(val); - } -} - /* static */ inline bool isAlpha(const String8& string) { const size_t length = string.length(); @@ -230,8 +213,7 @@ void AaptLocaleValue::setVariant(const char* variantChars) { bool AaptLocaleValue::initFromFilterString(const String8& str) { // A locale (as specified in the filter) is an underscore separated name such // as "en_US", "en_Latn_US", or "en_US_POSIX". - Vector<String8> parts; - splitAndLowerCase(str.string(), &parts, '_'); + Vector<String8> parts = AaptUtil::splitAndLowerCase(str, '_'); const int numTags = parts.size(); bool valid = false; @@ -301,8 +283,7 @@ int AaptLocaleValue::initFromDirName(const Vector<String8>& parts, const int sta if (part[0] == 'b' && part[1] == '+') { // This is a "modified" BCP-47 language tag. Same semantics as BCP-47 tags, // except that the separator is "+" and not "-". - Vector<String8> subtags; - AaptLocaleValue::splitAndLowerCase(part.string(), &subtags, '+'); + Vector<String8> subtags = AaptUtil::splitAndLowerCase(part, '+'); subtags.removeItemsAt(0); if (subtags.size() == 1) { setLanguage(subtags[0]); @@ -438,1349 +419,46 @@ void AaptLocaleValue::writeTo(ResTable_config* out) const { } } - -/* static */ bool -AaptGroupEntry::parseFilterNamePart(const String8& part, int* axis, AxisValue* value) -{ - ResTable_config config; - memset(&config, 0, sizeof(ResTable_config)); - - // IMSI - MCC - if (getMccName(part.string(), &config)) { - *axis = AXIS_MCC; - value->intValue = config.mcc; - return true; - } - - // IMSI - MNC - if (getMncName(part.string(), &config)) { - *axis = AXIS_MNC; - value->intValue = config.mnc; - return true; - } - - // locale - language - if (value->localeValue.initFromFilterString(part)) { - *axis = AXIS_LOCALE; - return true; - } - - // layout direction - if (getLayoutDirectionName(part.string(), &config)) { - *axis = AXIS_LAYOUTDIR; - value->intValue = (config.screenLayout&ResTable_config::MASK_LAYOUTDIR); - return true; - } - - // smallest screen dp width - if (getSmallestScreenWidthDpName(part.string(), &config)) { - *axis = AXIS_SMALLESTSCREENWIDTHDP; - value->intValue = config.smallestScreenWidthDp; - return true; - } - - // screen dp width - if (getScreenWidthDpName(part.string(), &config)) { - *axis = AXIS_SCREENWIDTHDP; - value->intValue = config.screenWidthDp; - return true; - } - - // screen dp height - if (getScreenHeightDpName(part.string(), &config)) { - *axis = AXIS_SCREENHEIGHTDP; - value->intValue = config.screenHeightDp; - return true; - } - - // screen layout size - if (getScreenLayoutSizeName(part.string(), &config)) { - *axis = AXIS_SCREENLAYOUTSIZE; - value->intValue = (config.screenLayout&ResTable_config::MASK_SCREENSIZE); - return true; - } - - // screen layout long - if (getScreenLayoutLongName(part.string(), &config)) { - *axis = AXIS_SCREENLAYOUTLONG; - value->intValue = (config.screenLayout&ResTable_config::MASK_SCREENLONG); - return true; - } - - // orientation - if (getOrientationName(part.string(), &config)) { - *axis = AXIS_ORIENTATION; - value->intValue = config.orientation; - return true; - } - - // ui mode type - if (getUiModeTypeName(part.string(), &config)) { - *axis = AXIS_UIMODETYPE; - value->intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE); - return true; - } - - // ui mode night - if (getUiModeNightName(part.string(), &config)) { - *axis = AXIS_UIMODENIGHT; - value->intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT); - return true; - } - - // density - if (getDensityName(part.string(), &config)) { - *axis = AXIS_DENSITY; - value->intValue = config.density; - return true; - } - - // touchscreen - if (getTouchscreenName(part.string(), &config)) { - *axis = AXIS_TOUCHSCREEN; - value->intValue = config.touchscreen; - return true; - } - - // keyboard hidden - if (getKeysHiddenName(part.string(), &config)) { - *axis = AXIS_KEYSHIDDEN; - value->intValue = config.inputFlags; - return true; - } - - // keyboard - if (getKeyboardName(part.string(), &config)) { - *axis = AXIS_KEYBOARD; - value->intValue = config.keyboard; - return true; - } - - // navigation hidden - if (getNavHiddenName(part.string(), &config)) { - *axis = AXIS_NAVHIDDEN; - value->intValue = config.inputFlags; - return 0; - } - - // navigation - if (getNavigationName(part.string(), &config)) { - *axis = AXIS_NAVIGATION; - value->intValue = config.navigation; - return true; - } - - // screen size - if (getScreenSizeName(part.string(), &config)) { - *axis = AXIS_SCREENSIZE; - value->intValue = config.screenSize; - return true; - } - - // version - if (getVersionName(part.string(), &config)) { - *axis = AXIS_VERSION; - value->intValue = config.version; - return true; - } - - return false; -} - -AxisValue -AaptGroupEntry::getConfigValueForAxis(const ResTable_config& config, int axis) -{ - AxisValue value; - switch (axis) { - case AXIS_MCC: - value.intValue = config.mcc; - break; - case AXIS_MNC: - value.intValue = config.mnc; - break; - case AXIS_LOCALE: - value.localeValue.initFromResTable(config); - break; - case AXIS_LAYOUTDIR: - value.intValue = config.screenLayout&ResTable_config::MASK_LAYOUTDIR; - break; - case AXIS_SCREENLAYOUTSIZE: - value.intValue = config.screenLayout&ResTable_config::MASK_SCREENSIZE; - break; - case AXIS_ORIENTATION: - value.intValue = config.orientation; - break; - case AXIS_UIMODETYPE: - value.intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE); - break; - case AXIS_UIMODENIGHT: - value.intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT); - break; - case AXIS_DENSITY: - value.intValue = config.density; - break; - case AXIS_TOUCHSCREEN: - value.intValue = config.touchscreen; - break; - case AXIS_KEYSHIDDEN: - value.intValue = config.inputFlags; - break; - case AXIS_KEYBOARD: - value.intValue = config.keyboard; - break; - case AXIS_NAVIGATION: - value.intValue = config.navigation; - break; - case AXIS_SCREENSIZE: - value.intValue = config.screenSize; - break; - case AXIS_SMALLESTSCREENWIDTHDP: - value.intValue = config.smallestScreenWidthDp; - break; - case AXIS_SCREENWIDTHDP: - value.intValue = config.screenWidthDp; - break; - case AXIS_SCREENHEIGHTDP: - value.intValue = config.screenHeightDp; - break; - case AXIS_VERSION: - value.intValue = config.version; - break; - } - - return value; -} - -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; - AaptLocaleValue::splitAndLowerCase(dir, &parts, '-'); - - String8 mcc, mnc, layoutsize, layoutlong, orient, den; - String8 touch, key, keysHidden, nav, navHidden, size, layoutDir, vers; - String8 uiModeType, uiModeNight, smallestwidthdp, widthdp, heightdp; - - AaptLocaleValue locale; - int numLocaleComponents = 0; - - const int N = parts.size(); - int index = 0; - String8 part = parts[index]; - - // resource type - if (!isValidResourceType(part)) { - return false; - } - *resType = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - - // imsi - mcc - if (getMccName(part.string())) { - mcc = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; + const char* q = strchr(dir, '-'); + size_t typeLen; + if (q != NULL) { + typeLen = q - dir; } else { - //printf("not mcc: %s\n", part.string()); + typeLen = strlen(dir); } - // imsi - mnc - if (getMncName(part.string())) { - mnc = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not mnc: %s\n", part.string()); - } - - index = locale.initFromDirName(parts, index); - if (index == -1) { + String8 type(dir, typeLen); + if (!isValidResourceType(type)) { return false; } - if (index >= N){ - goto success; - } - - part = parts[index]; - if (getLayoutDirectionName(part.string())) { - layoutDir = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not layout direction: %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; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not screen layout size: %s\n", part.string()); - } - - if (getScreenLayoutLongName(part.string())) { - layoutlong = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not screen layout long: %s\n", part.string()); - } - - // orientation - if (getOrientationName(part.string())) { - orient = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //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; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not density: %s\n", part.string()); - } - - // touchscreen - if (getTouchscreenName(part.string())) { - touch = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not touchscreen: %s\n", part.string()); - } - - // keyboard hidden - if (getKeysHiddenName(part.string())) { - keysHidden = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not keysHidden: %s\n", part.string()); - } - - // keyboard - if (getKeyboardName(part.string())) { - key = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //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; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not navigation: %s\n", part.string()); - } - - if (getScreenSizeName(part.string())) { - size = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not screen size: %s\n", part.string()); - } - - if (getVersionName(part.string())) { - vers = part; - - index++; - if (index == N) { - goto success; + if (q != NULL) { + if (!AaptConfig::parse(String8(q + 1), &mParams)) { + return false; } - part = parts[index]; - } else { - //printf("not version: %s\n", part.string()); } - // if there are extra parts, it doesn't match - return false; - -success: - this->mcc = mcc; - this->mnc = mnc; - this->locale = locale; - 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->layoutDirection = layoutDir; - this->version = vers; - - // what is this anyway? - this->vendor = ""; - + *resType = type; return true; } String8 -AaptGroupEntry::toString() const -{ - String8 s = this->mcc; - s += ","; - s += this->mnc; - s += ","; - s += locale.toDirName(); - s += ","; - s += layoutDirection; - 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; - s += ","; - s += keysHidden; - s += ","; - s += keyboard; - s += ","; - s += navHidden; - s += ","; - s += navigation; - s += ","; - s += screenSize; - s += ","; - s += version; - return s; -} - -String8 AaptGroupEntry::toDirName(const String8& resType) const { String8 s = resType; - if (this->mcc != "") { - if (s.length() > 0) { - s += "-"; - } - s += mcc; - } - if (this->mnc != "") { - if (s.length() > 0) { - s += "-"; - } - s += mnc; - } - - const String8 localeComponent = locale.toDirName(); - if (localeComponent != "") { - if (s.length() > 0) { - s += "-"; - } - s += localeComponent; - } - - if (this->layoutDirection != "") { - if (s.length() > 0) { - s += "-"; - } - s += layoutDirection; - } - 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 != "") { - if (s.length() > 0) { - s += "-"; - } - s += screenLayoutSize; - } - if (this->screenLayoutLong != "") { - if (s.length() > 0) { - s += "-"; - } - s += screenLayoutLong; - } - if (this->orientation != "") { - 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 != "") { - if (s.length() > 0) { - s += "-"; - } - s += density; - } - if (this->touchscreen != "") { - if (s.length() > 0) { - s += "-"; - } - s += touchscreen; - } - if (this->keysHidden != "") { - if (s.length() > 0) { - s += "-"; - } - s += keysHidden; - } - if (this->keyboard != "") { - if (s.length() > 0) { - s += "-"; - } - s += keyboard; - } - if (this->navHidden != "") { - if (s.length() > 0) { - s += "-"; - } - s += navHidden; - } - if (this->navigation != "") { - if (s.length() > 0) { - s += "-"; - } - s += navigation; - } - if (this->screenSize != "") { - if (s.length() > 0) { - s += "-"; - } - s += screenSize; - } - if (this->version != "") { + String8 params = mParams.toString(); + if (params.length() > 0) { if (s.length() > 0) { s += "-"; } - s += version; + s += params; } - return s; } -bool AaptGroupEntry::getMccName(const char* name, - ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->mcc = 0; - return true; - } - const char* c = name; - if (tolower(*c) != 'm') return false; - c++; - if (tolower(*c) != 'c') return false; - c++; - if (tolower(*c) != 'c') return false; - c++; - - const char* val = c; - - while (*c >= '0' && *c <= '9') { - c++; - } - if (*c != 0) return false; - if (c-val != 3) return false; - - int d = atoi(val); - if (d != 0) { - if (out) out->mcc = d; - return true; - } - - return false; -} - -bool AaptGroupEntry::getMncName(const char* name, - ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->mcc = 0; - return true; - } - const char* c = name; - if (tolower(*c) != 'm') return false; - c++; - if (tolower(*c) != 'n') return false; - c++; - if (tolower(*c) != 'c') return false; - c++; - - const char* val = c; - - while (*c >= '0' && *c <= '9') { - c++; - } - if (*c != 0) return false; - if (c-val == 0 || c-val > 3) return false; - - if (out) { - out->mnc = atoi(val); - if (out->mnc == 0) { - out->mnc = ACONFIGURATION_MNC_ZERO; - } - } - - return true; -} - -bool AaptGroupEntry::getLayoutDirectionName(const char* name, ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->screenLayout = - (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR) - | ResTable_config::LAYOUTDIR_ANY; - return true; - } else if (strcmp(name, "ldltr") == 0) { - if (out) out->screenLayout = - (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR) - | ResTable_config::LAYOUTDIR_LTR; - return true; - } else if (strcmp(name, "ldrtl") == 0) { - if (out) out->screenLayout = - (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR) - | ResTable_config::LAYOUTDIR_RTL; - return true; - } - - return false; -} - -bool AaptGroupEntry::getScreenLayoutSizeName(const char* name, - ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->screenLayout = - (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) - | ResTable_config::SCREENSIZE_ANY; - return true; - } else if (strcmp(name, "small") == 0) { - if (out) out->screenLayout = - (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) - | ResTable_config::SCREENSIZE_SMALL; - return true; - } else if (strcmp(name, "normal") == 0) { - if (out) out->screenLayout = - (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) - | ResTable_config::SCREENSIZE_NORMAL; - return true; - } else if (strcmp(name, "large") == 0) { - if (out) out->screenLayout = - (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; -} - -bool AaptGroupEntry::getScreenLayoutLongName(const char* name, - ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->screenLayout = - (out->screenLayout&~ResTable_config::MASK_SCREENLONG) - | ResTable_config::SCREENLONG_ANY; - return true; - } else if (strcmp(name, "long") == 0) { - if (out) out->screenLayout = - (out->screenLayout&~ResTable_config::MASK_SCREENLONG) - | ResTable_config::SCREENLONG_YES; - return true; - } else if (strcmp(name, "notlong") == 0) { - if (out) out->screenLayout = - (out->screenLayout&~ResTable_config::MASK_SCREENLONG) - | ResTable_config::SCREENLONG_NO; - return true; - } - - return false; -} - -bool AaptGroupEntry::getOrientationName(const char* name, - ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->orientation = out->ORIENTATION_ANY; - return true; - } else if (strcmp(name, "port") == 0) { - if (out) out->orientation = out->ORIENTATION_PORT; - return true; - } else if (strcmp(name, "land") == 0) { - if (out) out->orientation = out->ORIENTATION_LAND; - return true; - } else if (strcmp(name, "square") == 0) { - if (out) out->orientation = out->ORIENTATION_SQUARE; - return true; - } - - 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; - } else if (strcmp(name, "appliance") == 0) { - if (out) out->uiMode = - (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) - | ResTable_config::UI_MODE_TYPE_APPLIANCE; - return true; - } else if (strcmp(name, "watch") == 0) { - if (out) out->uiMode = - (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) - | ResTable_config::UI_MODE_TYPE_WATCH; - 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) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->density = ResTable_config::DENSITY_DEFAULT; - return true; - } - - if (strcmp(name, "nodpi") == 0) { - if (out) out->density = ResTable_config::DENSITY_NONE; - return true; - } - - if (strcmp(name, "ldpi") == 0) { - if (out) out->density = ResTable_config::DENSITY_LOW; - return true; - } - - if (strcmp(name, "mdpi") == 0) { - if (out) out->density = ResTable_config::DENSITY_MEDIUM; - 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_XHIGH; - return true; - } - - if (strcmp(name, "xxhdpi") == 0) { - if (out) out->density = ResTable_config::DENSITY_XXHIGH; - return true; - } - - if (strcmp(name, "xxxhdpi") == 0) { - if (out) out->density = ResTable_config::DENSITY_XXXHIGH; - return true; - } - - char* c = (char*)name; - while (*c >= '0' && *c <= '9') { - c++; - } - - // check that we have 'dpi' after the last digit. - if (toupper(c[0]) != 'D' || - toupper(c[1]) != 'P' || - toupper(c[2]) != 'I' || - c[3] != 0) { - return false; - } - - // temporarily replace the first letter with \0 to - // use atoi. - char tmp = c[0]; - c[0] = '\0'; - - int d = atoi(name); - c[0] = tmp; - - if (d != 0) { - if (out) out->density = d; - return true; - } - - return false; -} - -bool AaptGroupEntry::getTouchscreenName(const char* name, - ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->touchscreen = out->TOUCHSCREEN_ANY; - return true; - } else if (strcmp(name, "notouch") == 0) { - if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH; - return true; - } else if (strcmp(name, "stylus") == 0) { - if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS; - return true; - } else if (strcmp(name, "finger") == 0) { - if (out) out->touchscreen = out->TOUCHSCREEN_FINGER; - return true; - } - - return false; -} - -bool AaptGroupEntry::getKeysHiddenName(const char* name, - ResTable_config* out) -{ - uint8_t mask = 0; - uint8_t value = 0; - if (strcmp(name, kWildcardName) == 0) { - mask = ResTable_config::MASK_KEYSHIDDEN; - value = ResTable_config::KEYSHIDDEN_ANY; - } else if (strcmp(name, "keysexposed") == 0) { - mask = ResTable_config::MASK_KEYSHIDDEN; - value = ResTable_config::KEYSHIDDEN_NO; - } else if (strcmp(name, "keyshidden") == 0) { - mask = ResTable_config::MASK_KEYSHIDDEN; - value = ResTable_config::KEYSHIDDEN_YES; - } else if (strcmp(name, "keyssoft") == 0) { - mask = ResTable_config::MASK_KEYSHIDDEN; - value = ResTable_config::KEYSHIDDEN_SOFT; - } - - if (mask != 0) { - if (out) out->inputFlags = (out->inputFlags&~mask) | value; - return true; - } - - return false; -} - -bool AaptGroupEntry::getKeyboardName(const char* name, - ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->keyboard = out->KEYBOARD_ANY; - return true; - } else if (strcmp(name, "nokeys") == 0) { - if (out) out->keyboard = out->KEYBOARD_NOKEYS; - return true; - } else if (strcmp(name, "qwerty") == 0) { - if (out) out->keyboard = out->KEYBOARD_QWERTY; - return true; - } else if (strcmp(name, "12key") == 0) { - if (out) out->keyboard = out->KEYBOARD_12KEY; - return true; - } - - 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) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->navigation = out->NAVIGATION_ANY; - return true; - } else if (strcmp(name, "nonav") == 0) { - if (out) out->navigation = out->NAVIGATION_NONAV; - return true; - } else if (strcmp(name, "dpad") == 0) { - if (out) out->navigation = out->NAVIGATION_DPAD; - return true; - } else if (strcmp(name, "trackball") == 0) { - if (out) out->navigation = out->NAVIGATION_TRACKBALL; - return true; - } else if (strcmp(name, "wheel") == 0) { - if (out) out->navigation = out->NAVIGATION_WHEEL; - return true; - } - - return false; -} - -bool AaptGroupEntry::getScreenSizeName(const char* name, ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) { - out->screenWidth = out->SCREENWIDTH_ANY; - out->screenHeight = out->SCREENHEIGHT_ANY; - } - return true; - } - - const char* x = name; - while (*x >= '0' && *x <= '9') x++; - if (x == name || *x != 'x') return false; - String8 xName(name, x-name); - x++; - - const char* y = x; - while (*y >= '0' && *y <= '9') y++; - if (y == name || *y != 0) return false; - String8 yName(x, y-x); - - uint16_t w = (uint16_t)atoi(xName.string()); - uint16_t h = (uint16_t)atoi(yName.string()); - if (w < h) { - return false; - } - - if (out) { - out->screenWidth = w; - out->screenHeight = h; - } - - return true; -} - -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) { - out->sdkVersion = out->SDKVERSION_ANY; - out->minorVersion = out->MINORVERSION_ANY; - } - return true; - } - - if (*name != 'v') { - return false; - } - - name++; - const char* s = name; - while (*s >= '0' && *s <= '9') s++; - if (s == name || *s != 0) return false; - String8 sdkName(name, s-name); - - if (out) { - out->sdkVersion = (uint16_t)atoi(sdkName.string()); - out->minorVersion = 0; - } - - return true; -} - -int AaptGroupEntry::compare(const AaptGroupEntry& o) const -{ - int v = mcc.compare(o.mcc); - if (v == 0) v = mnc.compare(o.mnc); - if (v == 0) v = locale.compare(o.locale); - if (v == 0) v = layoutDirection.compare(o.layoutDirection); - 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; -} - -const ResTable_config AaptGroupEntry::toParams() const -{ - if (!mParamsChanged) { - return mParams; - } - - mParamsChanged = false; - ResTable_config& params = mParams; - memset(¶ms, 0, sizeof(ResTable_config)); - getMccName(mcc.string(), ¶ms); - getMncName(mnc.string(), ¶ms); - locale.writeTo(¶ms); - getLayoutDirectionName(layoutDirection.string(), ¶ms); - getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), ¶ms); - getScreenWidthDpName(screenWidthDp.string(), ¶ms); - getScreenHeightDpName(screenHeightDp.string(), ¶ms); - getScreenLayoutSizeName(screenLayoutSize.string(), ¶ms); - getScreenLayoutLongName(screenLayoutLong.string(), ¶ms); - getOrientationName(orientation.string(), ¶ms); - getUiModeTypeName(uiModeType.string(), ¶ms); - getUiModeNightName(uiModeNight.string(), ¶ms); - getDensityName(density.string(), ¶ms); - getTouchscreenName(touchscreen.string(), ¶ms); - getKeysHiddenName(keysHidden.string(), ¶ms); - getKeyboardName(keyboard.string(), ¶ms); - getNavHiddenName(navHidden.string(), ¶ms); - getNavigationName(navigation.string(), ¶ms); - getScreenSizeName(screenSize.string(), ¶ms); - getVersionName(version.string(), ¶ms); - - // 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; -} // ========================================================================= // ========================================================================= @@ -1803,6 +481,11 @@ void* AaptFile::editData(size_t size) return buf; } +void* AaptFile::editDataInRange(size_t offset, size_t size) +{ + return (void*)(((uint8_t*) editData(offset + size)) + offset); +} + void* AaptFile::editData(size_t* outSize) { if (outSize) { @@ -2224,9 +907,7 @@ AaptAssets::AaptAssets() : AaptDir(String8(), String8()), mHavePrivateSymbols(false), mChanged(false), mHaveIncludedAssets(false), - mRes(NULL) -{ -} + mRes(NULL) {} const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const { if (mChanged) { @@ -2383,6 +1064,9 @@ ssize_t AaptAssets::slurpFromArgs(Bundle* bundle) current->setFullResPaths(mFullResPaths); } count = current->slurpResourceTree(bundle, String8(res)); + if (i > 0 && count > 0) { + count = current->filter(bundle); + } if (count < 0) { totalCount = count; @@ -2498,7 +1182,7 @@ ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir) String8 resType; bool b = group.initFromDirName(entry->d_name, &resType); if (!b) { - fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(), + fprintf(stderr, "invalid resource directory name: %s %s\n", srcDir.string(), entry->d_name); err = -1; continue; @@ -2646,30 +1330,35 @@ bail: status_t AaptAssets::filter(Bundle* bundle) { - ResourceFilter reqFilter; + WeakResourceFilter 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; + uint32_t preferredDensity = 0; + if (bundle->getPreferredDensity().size() > 0) { + ResTable_config preferredConfig; + if (!AaptConfig::parseDensity(bundle->getPreferredDensity().string(), &preferredConfig)) { + fprintf(stderr, "Error parsing preferred density: %s\n", + bundle->getPreferredDensity().string()); + return UNKNOWN_ERROR; + } + preferredDensity = preferredConfig.density; } - if (reqFilter.isEmpty() && prefFilter.isEmpty()) { + if (reqFilter.isEmpty() && preferredDensity == 0) { return NO_ERROR; } if (bundle->getVerbose()) { if (!reqFilter.isEmpty()) { printf("Applying required filter: %s\n", - bundle->getConfigurations()); + bundle->getConfigurations().string()); } - if (!prefFilter.isEmpty()) { - printf("Applying preferred filter: %s\n", - bundle->getPreferredConfigurations()); + if (preferredDensity > 0) { + printf("Applying preferred density filter: %s\n", + bundle->getPreferredDensity().string()); } } @@ -2726,88 +1415,70 @@ status_t AaptAssets::filter(Bundle* bundle) } // Quick check: no preferred filters, nothing more to do. - if (prefFilter.isEmpty()) { + if (preferredDensity == 0) { continue; } // Get the preferred density if there is one. We do not match exactly for density. // If our preferred density is hdpi but we only have mdpi and xhdpi resources, we // pick xhdpi. - uint32_t preferredDensity = 0; - const SortedVector<AxisValue>* preferredConfigs = prefFilter.configsForAxis(AXIS_DENSITY); - if (preferredConfigs != NULL && preferredConfigs->size() > 0) { - preferredDensity = (*preferredConfigs)[0].intValue; - } + 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 (config.density != 0 && config.density != preferredDensity) { + // 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. + uint32_t bestDensity = config.density; + + for (size_t m=0; m<grp->getFiles().size(); m++) { + if (m == k) { + 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. - - uint32_t bestDensity = config.density; - - 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 (axis == AXIS_DENSITY && preferredDensity > 0) { - // See if there is a better density resource - if (mconfig.density < bestDensity && - mconfig.density > preferredDensity && - bestDensity > preferredDensity) { - // This density is between our best density and - // the preferred density, therefore it is better. - bestDensity = mconfig.density; - } else if (mconfig.density > bestDensity && - bestDensity < preferredDensity) { - // This density is better than our best density and - // our best density was smaller than our preferred - // density, so it is better. - bestDensity = mconfig.density; - } - } else if (prefFilter.match(axis, mconfig)) { - if (bundle->getVerbose()) { - printf("Pruning unneeded resource: %s\n", - file->getPrintableSource().string()); - } - grp->removeFile(k); - k--; - break; - } + sp<AaptFile> mfile = grp->getFiles().valueAt(m); + const ResTable_config& mconfig(mfile->getGroupEntry().toParams()); + if (AaptConfig::isSameExcept(config, mconfig, ResTable_config::CONFIG_DENSITY)) { + // See if there is a better density resource + if (mconfig.density < bestDensity && + mconfig.density > preferredDensity && + bestDensity > preferredDensity) { + // This density is between our best density and + // the preferred density, therefore it is better. + bestDensity = mconfig.density; + } else if (mconfig.density > bestDensity && + bestDensity < preferredDensity) { + // This density is better than our best density and + // our best density was smaller than our preferred + // density, so it is better. + bestDensity = mconfig.density; } } + } - if (axis == AXIS_DENSITY && preferredDensity > 0 && - bestDensity != config.density) { - if (bundle->getVerbose()) { - printf("Pruning unneeded resource: %s\n", - file->getPrintableSource().string()); - } - grp->removeFile(k); - k--; + if (bestDensity != config.density) { + if (bundle->getVerbose()) { + printf("Pruning unneeded resource: %s\n", + file->getPrintableSource().string()); } + grp->removeFile(k); + k--; } } } diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h index 336d08b..0c2576a 100644 --- a/tools/aapt/AaptAssets.h +++ b/tools/aapt/AaptAssets.h @@ -6,22 +6,24 @@ #ifndef __AAPT_ASSETS_H #define __AAPT_ASSETS_H -#include <stdlib.h> #include <androidfw/AssetManager.h> #include <androidfw/ResourceTypes.h> +#include <stdlib.h> +#include <set> #include <utils/KeyedVector.h> #include <utils/RefBase.h> #include <utils/SortedVector.h> #include <utils/String8.h> #include <utils/Vector.h> -#include "ZipFile.h" +#include "AaptConfig.h" #include "Bundle.h" +#include "ConfigDescription.h" #include "SourcePos.h" +#include "ZipFile.h" using namespace android; - extern const char * const gDefaultIgnoreAssets; extern const char * gUserIgnoreAssets; @@ -82,9 +84,6 @@ struct AaptLocaleValue { return memcmp(this, &other, sizeof(AaptLocaleValue)); } - static void splitAndLowerCase(const char* const chars, Vector<String8>* parts, - const char separator); - inline bool operator<(const AaptLocaleValue& o) const { return compare(o) < 0; } inline bool operator<=(const AaptLocaleValue& o) const { return compare(o) <= 0; } inline bool operator==(const AaptLocaleValue& o) const { return compare(o) == 0; } @@ -98,31 +97,6 @@ private: void setVariant(const char* variant); }; -struct AxisValue { - // Used for all axes except AXIS_LOCALE, which is represented - // as a AaptLocaleValue value. - int intValue; - AaptLocaleValue localeValue; - - AxisValue() : intValue(0) { - } - - inline int compare(const AxisValue &other) const { - if (intValue != other.intValue) { - return intValue - other.intValue; - } - - return localeValue.compare(other.localeValue); - } - - inline bool operator<(const AxisValue& o) const { return compare(o) < 0; } - inline bool operator<=(const AxisValue& o) const { return compare(o) <= 0; } - inline bool operator==(const AxisValue& o) const { return compare(o) == 0; } - inline bool operator!=(const AxisValue& o) const { return compare(o) != 0; } - inline bool operator>=(const AxisValue& o) const { return compare(o) >= 0; } - inline bool operator>(const AxisValue& o) const { return compare(o) > 0; } -}; - /** * This structure contains a specific variation of a single file out * of all the variations it can have that we can have. @@ -130,23 +104,11 @@ struct AxisValue { struct AaptGroupEntry { public: - AaptGroupEntry() : mParamsChanged(true) { - memset(&mParams, 0, sizeof(ResTable_config)); - } - bool initFromDirName(const char* dir, String8* resType); - static bool parseFilterNamePart(const String8& part, int* axis, AxisValue* value); - - static AxisValue getConfigValueForAxis(const ResTable_config& config, int axis); - - static bool configSameExcept(const ResTable_config& config, - const ResTable_config& otherConfig, int axis); - - int compare(const AaptGroupEntry& o) const; - - const ResTable_config toParams() const; + inline const ConfigDescription& toParams() const { return mParams; } + inline int compare(const AaptGroupEntry& o) const { return mParams.compareLogical(o.mParams); } inline bool operator<(const AaptGroupEntry& o) const { return compare(o) < 0; } inline bool operator<=(const AaptGroupEntry& o) const { return compare(o) <= 0; } inline bool operator==(const AaptGroupEntry& o) const { return compare(o) == 0; } @@ -154,56 +116,13 @@ public: inline bool operator>=(const AaptGroupEntry& o) const { return compare(o) >= 0; } inline bool operator>(const AaptGroupEntry& o) const { return compare(o) > 0; } - String8 toString() const; + String8 toString() const { return mParams.toString(); } String8 toDirName(const String8& resType) const; - const String8& getVersionString() const { return version; } + const String8 getVersionString() const { return AaptConfig::getVersion(mParams); } private: - static bool getMccName(const char* name, ResTable_config* out = NULL); - static bool getMncName(const char* name, ResTable_config* out = NULL); - static bool getScreenLayoutSizeName(const char* name, ResTable_config* out = NULL); - static bool getScreenLayoutLongName(const char* name, ResTable_config* out = NULL); - static bool getOrientationName(const char* name, ResTable_config* out = NULL); - static bool getUiModeTypeName(const char* name, ResTable_config* out = NULL); - static bool getUiModeNightName(const char* name, ResTable_config* out = NULL); - static bool getDensityName(const char* name, ResTable_config* out = NULL); - static bool getTouchscreenName(const char* name, ResTable_config* out = NULL); - static bool getKeysHiddenName(const char* name, ResTable_config* out = NULL); - static bool getKeyboardName(const char* name, ResTable_config* out = NULL); - 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 getLayoutDirectionName(const char* name, ResTable_config* out = NULL); - static bool getVersionName(const char* name, ResTable_config* out = NULL); - - String8 mcc; - String8 mnc; - AaptLocaleValue 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 layoutDirection; - String8 version; - - mutable bool mParamsChanged; - mutable ResTable_config mParams; + ConfigDescription mParams; }; inline int compare_type(const AaptGroupEntry& lhs, const AaptGroupEntry& rhs) @@ -251,6 +170,7 @@ public: size_t getSize() const { return mDataSize; } void* editData(size_t size); void* editData(size_t* outSize = NULL); + void* editDataInRange(size_t offset, size_t size); void* padData(size_t wordSize); status_t writeData(const void* data, size_t size); void clearData(); diff --git a/tools/aapt/AaptConfig.cpp b/tools/aapt/AaptConfig.cpp new file mode 100644 index 0000000..69a9c7f --- /dev/null +++ b/tools/aapt/AaptConfig.cpp @@ -0,0 +1,790 @@ +/* + * Copyright (C) 2014 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +#include <androidfw/ResourceTypes.h> +#include <ctype.h> + +#include "AaptConfig.h" +#include "AaptAssets.h" +#include "AaptUtil.h" +#include "ResourceFilter.h" + +using android::String8; +using android::Vector; +using android::ResTable_config; + +namespace AaptConfig { + +static const char* kWildcardName = "any"; + +bool parse(const String8& str, ConfigDescription* out) { + Vector<String8> parts = AaptUtil::splitAndLowerCase(str, '-'); + + ConfigDescription config; + AaptLocaleValue locale; + ssize_t index = 0; + ssize_t localeIndex = 0; + const ssize_t N = parts.size(); + const char* part = parts[index].string(); + + if (str.length() == 0) { + goto success; + } + + if (parseMcc(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseMnc(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + // Locale spans a few '-' separators, so we let it + // control the index. + localeIndex = locale.initFromDirName(parts, index); + if (localeIndex < 0) { + return false; + } else if (localeIndex > index) { + locale.writeTo(&config); + index = localeIndex; + if (index >= N) { + goto success; + } + part = parts[index].string(); + } + + if (parseLayoutDirection(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseSmallestScreenWidthDp(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseScreenWidthDp(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseScreenHeightDp(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseScreenLayoutSize(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseScreenLayoutLong(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseOrientation(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseUiModeType(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseUiModeNight(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseDensity(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseTouchscreen(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseKeysHidden(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseKeyboard(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseNavHidden(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseNavigation(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseScreenSize(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseVersion(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + // Unrecognized. + return false; + +success: + if (out != NULL) { + applyVersionForCompatibility(&config); + *out = config; + } + return true; +} + +bool parseCommaSeparatedList(const String8& str, std::set<ConfigDescription>* outSet) { + Vector<String8> parts = AaptUtil::splitAndLowerCase(str, ','); + const size_t N = parts.size(); + for (size_t i = 0; i < N; i++) { + ConfigDescription config; + if (!parse(parts[i], &config)) { + return false; + } + outSet->insert(config); + } + return true; +} + +void applyVersionForCompatibility(ConfigDescription* config) { + if (config == NULL) { + return; + } + + uint16_t minSdk = 0; + if (config->smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY + || config->screenWidthDp != ResTable_config::SCREENWIDTH_ANY + || config->screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) { + minSdk = SDK_HONEYCOMB_MR2; + } else if ((config->uiMode & ResTable_config::MASK_UI_MODE_TYPE) + != ResTable_config::UI_MODE_TYPE_ANY + || (config->uiMode & ResTable_config::MASK_UI_MODE_NIGHT) + != ResTable_config::UI_MODE_NIGHT_ANY) { + minSdk = SDK_FROYO; + } else if ((config->screenLayout & ResTable_config::MASK_SCREENSIZE) + != ResTable_config::SCREENSIZE_ANY + || (config->screenLayout & ResTable_config::MASK_SCREENLONG) + != ResTable_config::SCREENLONG_ANY + || config->density != ResTable_config::DENSITY_DEFAULT) { + minSdk = SDK_DONUT; + } + + if (minSdk > config->sdkVersion) { + config->sdkVersion = minSdk; + } +} + +bool parseMcc(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->mcc = 0; + return true; + } + const char* c = name; + if (tolower(*c) != 'm') return false; + c++; + if (tolower(*c) != 'c') return false; + c++; + if (tolower(*c) != 'c') return false; + c++; + + const char* val = c; + + while (*c >= '0' && *c <= '9') { + c++; + } + if (*c != 0) return false; + if (c-val != 3) return false; + + int d = atoi(val); + if (d != 0) { + if (out) out->mcc = d; + return true; + } + + return false; +} + +bool parseMnc(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->mcc = 0; + return true; + } + const char* c = name; + if (tolower(*c) != 'm') return false; + c++; + if (tolower(*c) != 'n') return false; + c++; + if (tolower(*c) != 'c') return false; + c++; + + const char* val = c; + + while (*c >= '0' && *c <= '9') { + c++; + } + if (*c != 0) return false; + if (c-val == 0 || c-val > 3) return false; + + if (out) { + out->mnc = atoi(val); + if (out->mnc == 0) { + out->mnc = ACONFIGURATION_MNC_ZERO; + } + } + + return true; +} + +bool parseLayoutDirection(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->screenLayout = + (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR) + | ResTable_config::LAYOUTDIR_ANY; + return true; + } else if (strcmp(name, "ldltr") == 0) { + if (out) out->screenLayout = + (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR) + | ResTable_config::LAYOUTDIR_LTR; + return true; + } else if (strcmp(name, "ldrtl") == 0) { + if (out) out->screenLayout = + (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR) + | ResTable_config::LAYOUTDIR_RTL; + return true; + } + + return false; +} + +bool parseScreenLayoutSize(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->screenLayout = + (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) + | ResTable_config::SCREENSIZE_ANY; + return true; + } else if (strcmp(name, "small") == 0) { + if (out) out->screenLayout = + (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) + | ResTable_config::SCREENSIZE_SMALL; + return true; + } else if (strcmp(name, "normal") == 0) { + if (out) out->screenLayout = + (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) + | ResTable_config::SCREENSIZE_NORMAL; + return true; + } else if (strcmp(name, "large") == 0) { + if (out) out->screenLayout = + (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; +} + +bool parseScreenLayoutLong(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->screenLayout = + (out->screenLayout&~ResTable_config::MASK_SCREENLONG) + | ResTable_config::SCREENLONG_ANY; + return true; + } else if (strcmp(name, "long") == 0) { + if (out) out->screenLayout = + (out->screenLayout&~ResTable_config::MASK_SCREENLONG) + | ResTable_config::SCREENLONG_YES; + return true; + } else if (strcmp(name, "notlong") == 0) { + if (out) out->screenLayout = + (out->screenLayout&~ResTable_config::MASK_SCREENLONG) + | ResTable_config::SCREENLONG_NO; + return true; + } + + return false; +} + +bool parseOrientation(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->orientation = out->ORIENTATION_ANY; + return true; + } else if (strcmp(name, "port") == 0) { + if (out) out->orientation = out->ORIENTATION_PORT; + return true; + } else if (strcmp(name, "land") == 0) { + if (out) out->orientation = out->ORIENTATION_LAND; + return true; + } else if (strcmp(name, "square") == 0) { + if (out) out->orientation = out->ORIENTATION_SQUARE; + return true; + } + + return false; +} + +bool parseUiModeType(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; + } else if (strcmp(name, "appliance") == 0) { + if (out) out->uiMode = + (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) + | ResTable_config::UI_MODE_TYPE_APPLIANCE; + return true; + } else if (strcmp(name, "watch") == 0) { + if (out) out->uiMode = + (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) + | ResTable_config::UI_MODE_TYPE_WATCH; + return true; + } + + return false; +} + +bool parseUiModeNight(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 parseDensity(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->density = ResTable_config::DENSITY_DEFAULT; + return true; + } + + if (strcmp(name, "nodpi") == 0) { + if (out) out->density = ResTable_config::DENSITY_NONE; + return true; + } + + if (strcmp(name, "ldpi") == 0) { + if (out) out->density = ResTable_config::DENSITY_LOW; + return true; + } + + if (strcmp(name, "mdpi") == 0) { + if (out) out->density = ResTable_config::DENSITY_MEDIUM; + 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_XHIGH; + return true; + } + + if (strcmp(name, "xxhdpi") == 0) { + if (out) out->density = ResTable_config::DENSITY_XXHIGH; + return true; + } + + if (strcmp(name, "xxxhdpi") == 0) { + if (out) out->density = ResTable_config::DENSITY_XXXHIGH; + return true; + } + + char* c = (char*)name; + while (*c >= '0' && *c <= '9') { + c++; + } + + // check that we have 'dpi' after the last digit. + if (toupper(c[0]) != 'D' || + toupper(c[1]) != 'P' || + toupper(c[2]) != 'I' || + c[3] != 0) { + return false; + } + + // temporarily replace the first letter with \0 to + // use atoi. + char tmp = c[0]; + c[0] = '\0'; + + int d = atoi(name); + c[0] = tmp; + + if (d != 0) { + if (out) out->density = d; + return true; + } + + return false; +} + +bool parseTouchscreen(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->touchscreen = out->TOUCHSCREEN_ANY; + return true; + } else if (strcmp(name, "notouch") == 0) { + if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH; + return true; + } else if (strcmp(name, "stylus") == 0) { + if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS; + return true; + } else if (strcmp(name, "finger") == 0) { + if (out) out->touchscreen = out->TOUCHSCREEN_FINGER; + return true; + } + + return false; +} + +bool parseKeysHidden(const char* name, ResTable_config* out) { + uint8_t mask = 0; + uint8_t value = 0; + if (strcmp(name, kWildcardName) == 0) { + mask = ResTable_config::MASK_KEYSHIDDEN; + value = ResTable_config::KEYSHIDDEN_ANY; + } else if (strcmp(name, "keysexposed") == 0) { + mask = ResTable_config::MASK_KEYSHIDDEN; + value = ResTable_config::KEYSHIDDEN_NO; + } else if (strcmp(name, "keyshidden") == 0) { + mask = ResTable_config::MASK_KEYSHIDDEN; + value = ResTable_config::KEYSHIDDEN_YES; + } else if (strcmp(name, "keyssoft") == 0) { + mask = ResTable_config::MASK_KEYSHIDDEN; + value = ResTable_config::KEYSHIDDEN_SOFT; + } + + if (mask != 0) { + if (out) out->inputFlags = (out->inputFlags&~mask) | value; + return true; + } + + return false; +} + +bool parseKeyboard(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->keyboard = out->KEYBOARD_ANY; + return true; + } else if (strcmp(name, "nokeys") == 0) { + if (out) out->keyboard = out->KEYBOARD_NOKEYS; + return true; + } else if (strcmp(name, "qwerty") == 0) { + if (out) out->keyboard = out->KEYBOARD_QWERTY; + return true; + } else if (strcmp(name, "12key") == 0) { + if (out) out->keyboard = out->KEYBOARD_12KEY; + return true; + } + + return false; +} + +bool parseNavHidden(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 parseNavigation(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->navigation = out->NAVIGATION_ANY; + return true; + } else if (strcmp(name, "nonav") == 0) { + if (out) out->navigation = out->NAVIGATION_NONAV; + return true; + } else if (strcmp(name, "dpad") == 0) { + if (out) out->navigation = out->NAVIGATION_DPAD; + return true; + } else if (strcmp(name, "trackball") == 0) { + if (out) out->navigation = out->NAVIGATION_TRACKBALL; + return true; + } else if (strcmp(name, "wheel") == 0) { + if (out) out->navigation = out->NAVIGATION_WHEEL; + return true; + } + + return false; +} + +bool parseScreenSize(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) { + out->screenWidth = out->SCREENWIDTH_ANY; + out->screenHeight = out->SCREENHEIGHT_ANY; + } + return true; + } + + const char* x = name; + while (*x >= '0' && *x <= '9') x++; + if (x == name || *x != 'x') return false; + String8 xName(name, x-name); + x++; + + const char* y = x; + while (*y >= '0' && *y <= '9') y++; + if (y == name || *y != 0) return false; + String8 yName(x, y-x); + + uint16_t w = (uint16_t)atoi(xName.string()); + uint16_t h = (uint16_t)atoi(yName.string()); + if (w < h) { + return false; + } + + if (out) { + out->screenWidth = w; + out->screenHeight = h; + } + + return true; +} + +bool parseSmallestScreenWidthDp(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 parseScreenWidthDp(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 parseScreenHeightDp(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 parseVersion(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) { + out->sdkVersion = out->SDKVERSION_ANY; + out->minorVersion = out->MINORVERSION_ANY; + } + return true; + } + + if (*name != 'v') { + return false; + } + + name++; + const char* s = name; + while (*s >= '0' && *s <= '9') s++; + if (s == name || *s != 0) return false; + String8 sdkName(name, s-name); + + if (out) { + out->sdkVersion = (uint16_t)atoi(sdkName.string()); + out->minorVersion = 0; + } + + return true; +} + +String8 getVersion(const ResTable_config& config) { + return String8::format("v%u", config.sdkVersion); +} + +bool isSameExcept(const ResTable_config& a, const ResTable_config& b, int axisMask) { + return a.diff(b) == axisMask; +} + +} // namespace AaptConfig diff --git a/tools/aapt/AaptConfig.h b/tools/aapt/AaptConfig.h new file mode 100644 index 0000000..2963539 --- /dev/null +++ b/tools/aapt/AaptConfig.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2014 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +#ifndef __AAPT_CONFIG_H +#define __AAPT_CONFIG_H + +#include <set> +#include <utils/String8.h> + +#include "ConfigDescription.h" + +/** + * Utility methods for dealing with configurations. + */ +namespace AaptConfig { + +/** + * Parse a string of the form 'fr-sw600dp-land' and fill in the + * given ResTable_config with resulting configuration parameters. + * + * The resulting configuration has the appropriate sdkVersion defined + * for backwards compatibility. + */ +bool parse(const android::String8& str, ConfigDescription* out = NULL); + +/** + * Parse a comma separated list of configuration strings. Duplicate configurations + * will be removed. + * + * Example input: "fr,de-land,fr-sw600dp-land" + */ +bool parseCommaSeparatedList(const android::String8& str, std::set<ConfigDescription>* outSet); + +/** + * If the configuration uses an axis that was added after + * the original Android release, make sure the SDK version + * is set accordingly. + */ +void applyVersionForCompatibility(ConfigDescription* config); + +// Individual axis +bool parseMcc(const char* str, android::ResTable_config* out = NULL); +bool parseMnc(const char* str, android::ResTable_config* out = NULL); +bool parseLayoutDirection(const char* str, android::ResTable_config* out = NULL); +bool parseSmallestScreenWidthDp(const char* str, android::ResTable_config* out = NULL); +bool parseScreenWidthDp(const char* str, android::ResTable_config* out = NULL); +bool parseScreenHeightDp(const char* str, android::ResTable_config* out = NULL); +bool parseScreenLayoutSize(const char* str, android::ResTable_config* out = NULL); +bool parseScreenLayoutLong(const char* str, android::ResTable_config* out = NULL); +bool parseOrientation(const char* str, android::ResTable_config* out = NULL); +bool parseUiModeType(const char* str, android::ResTable_config* out = NULL); +bool parseUiModeNight(const char* str, android::ResTable_config* out = NULL); +bool parseDensity(const char* str, android::ResTable_config* out = NULL); +bool parseTouchscreen(const char* str, android::ResTable_config* out = NULL); +bool parseKeysHidden(const char* str, android::ResTable_config* out = NULL); +bool parseKeyboard(const char* str, android::ResTable_config* out = NULL); +bool parseNavHidden(const char* str, android::ResTable_config* out = NULL); +bool parseNavigation(const char* str, android::ResTable_config* out = NULL); +bool parseScreenSize(const char* str, android::ResTable_config* out = NULL); +bool parseVersion(const char* str, android::ResTable_config* out = NULL); + +android::String8 getVersion(const android::ResTable_config& config); + +/** + * Returns true if the two configurations only differ by the specified axis. + * The axis mask is a bitmask of CONFIG_* constants. + */ +bool isSameExcept(const android::ResTable_config& a, const android::ResTable_config& b, int configMask); + +} // namespace AaptConfig + +#endif // __AAPT_CONFIG_H diff --git a/tools/aapt/AaptUtil.cpp b/tools/aapt/AaptUtil.cpp new file mode 100644 index 0000000..293e144 --- /dev/null +++ b/tools/aapt/AaptUtil.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2014 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +#include "AaptUtil.h" + +using android::Vector; +using android::String8; + +namespace AaptUtil { + +Vector<String8> split(const String8& str, const char sep) { + Vector<String8> parts; + const char* p = str.string(); + const char* q; + + while (true) { + q = strchr(p, sep); + if (q == NULL) { + parts.add(String8(p, strlen(p))); + return parts; + } + + parts.add(String8(p, q-p)); + p = q + 1; + } + return parts; +} + +Vector<String8> splitAndLowerCase(const String8& str, const char sep) { + Vector<String8> parts; + const char* p = str.string(); + const char* q; + + while (true) { + q = strchr(p, sep); + if (q == NULL) { + String8 val(p, strlen(p)); + val.toLower(); + parts.add(val); + return parts; + } + + String8 val(p, q-p); + val.toLower(); + parts.add(val); + p = q + 1; + } + return parts; +} + +} // namespace AaptUtil diff --git a/tools/aapt/AaptUtil.h b/tools/aapt/AaptUtil.h new file mode 100644 index 0000000..47a704a --- /dev/null +++ b/tools/aapt/AaptUtil.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2014 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +#ifndef __AAPT_UTIL_H +#define __AAPT_UTIL_H + +#include <utils/String8.h> +#include <utils/Vector.h> + +namespace AaptUtil { + +android::Vector<android::String8> split(const android::String8& str, const char sep); +android::Vector<android::String8> splitAndLowerCase(const android::String8& str, const char sep); + +} // namespace AaptUtil + +#endif // __AAPT_UTIL_H diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk index 806f8ff..700afa1 100644 --- a/tools/aapt/Android.mk +++ b/tools/aapt/Android.mk @@ -1,104 +1,168 @@ -# -# Copyright 2006 The Android Open Source Project # -# Android Asset Packaging Tool +# Copyright (C) 2014 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. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# 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. # # This tool is prebuilt if we're doing an app-only build. ifeq ($(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)),) +# ========================================================== +# Setup some common variables for the different build +# targets here. +# ========================================================== +LOCAL_PATH:= $(call my-dir) -aapt_src_files := \ - AaptAssets.cpp \ - Command.cpp \ - CrunchCache.cpp \ - FileFinder.cpp \ - Main.cpp \ - Package.cpp \ - StringPool.cpp \ - XMLNode.cpp \ - ResourceFilter.cpp \ - ResourceIdCache.cpp \ - ResourceTable.cpp \ - Images.cpp \ - Resource.cpp \ +aaptMain := Main.cpp +aaptSources := \ + AaptAssets.cpp \ + AaptConfig.cpp \ + AaptUtil.cpp \ + ApkBuilder.cpp \ + Command.cpp \ + CrunchCache.cpp \ + FileFinder.cpp \ + Package.cpp \ + StringPool.cpp \ + XMLNode.cpp \ + ResourceFilter.cpp \ + ResourceIdCache.cpp \ + ResourceTable.cpp \ + Images.cpp \ + Resource.cpp \ pseudolocalize.cpp \ SourcePos.cpp \ - WorkQueue.cpp \ + WorkQueue.cpp \ ZipEntry.cpp \ ZipFile.cpp \ - qsort_r_compat.c + qsort_r_compat.c + +aaptTests := \ + tests/AaptConfig_test.cpp \ + tests/AaptGroupEntry_test.cpp \ + tests/ResourceFilter_test.cpp + +aaptCIncludes := \ + external/libpng \ + external/zlib + +aaptHostLdLibs := +aaptHostStaticLibs := \ + libandroidfw \ + libpng \ + liblog \ + libutils \ + libcutils \ + libexpat \ + libziparchive-host -LOCAL_PATH:= $(call my-dir) +ifeq ($(HOST_OS),linux) + aaptHostLdLibs += -lrt -ldl -lpthread +endif + +# Statically link libz for MinGW (Win SDK under Linux), +# and dynamically link for all others. +ifneq ($(strip $(USE_MINGW)),) + aaptHostStaticLibs += libz +else + aaptHostLdLibs += -lz +endif + + +# ========================================================== +# Build the host static library: libaapt +# ========================================================== include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(aapt_src_files) +LOCAL_MODULE := libaapt + +LOCAL_SRC_FILES := $(aaptSources) +LOCAL_C_INCLUDES += $(aaptCIncludes) LOCAL_CFLAGS += -Wno-format-y2k +LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS ifeq (darwin,$(HOST_OS)) LOCAL_CFLAGS += -D_DARWIN_UNLIMITED_STREAMS endif -LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS +include $(BUILD_HOST_STATIC_LIBRARY) -LOCAL_C_INCLUDES += external/libpng -LOCAL_C_INCLUDES += external/zlib -LOCAL_STATIC_LIBRARIES := \ - libandroidfw \ - libutils \ - libcutils \ - libexpat \ - libpng \ - liblog \ - libziparchive-host +# ========================================================== +# Build the host executable: aapt +# ========================================================== +include $(CLEAR_VARS) -ifeq ($(HOST_OS),linux) -LOCAL_LDLIBS += -lrt -ldl -lpthread -endif +LOCAL_MODULE := aapt -# Statically link libz for MinGW (Win SDK under Linux), -# and dynamically link for all others. -ifneq ($(strip $(USE_MINGW)),) - LOCAL_STATIC_LIBRARIES += libz -else - LOCAL_LDLIBS += -lz -endif +LOCAL_SRC_FILES := $(aaptMain) -LOCAL_MODULE := aapt +LOCAL_STATIC_LIBRARIES += \ + libaapt \ + $(aaptHostStaticLibs) +LOCAL_LDLIBS += $(aaptHostLdLibs) include $(BUILD_HOST_EXECUTABLE) -# aapt for running on the device -# ========================================================= -ifneq ($(SDK_ONLY),true) + +# ========================================================== +# Build the host tests: libaapt_tests +# ========================================================== include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(aapt_src_files) +LOCAL_MODULE := libaapt_tests -LOCAL_MODULE := aapt +LOCAL_SRC_FILES += $(aaptTests) +LOCAL_C_INCLUDES += $(LOCAL_PATH) -LOCAL_C_INCLUDES += bionic -LOCAL_C_INCLUDES += bionic/libstdc++/include -LOCAL_C_INCLUDES += external/stlport/stlport -LOCAL_C_INCLUDES += external/libpng -LOCAL_C_INCLUDES += external/zlib +LOCAL_STATIC_LIBRARIES += \ + libaapt \ + $(aaptHostStaticLibs) +LOCAL_LDLIBS += $(aaptHostLdLibs) -LOCAL_CFLAGS += -Wno-non-virtual-dtor +include $(BUILD_HOST_NATIVE_TEST) + + +# ========================================================== +# Build the device executable: aapt +# ========================================================== +ifneq ($(SDK_ONLY),true) +include $(CLEAR_VARS) + +LOCAL_MODULE := aapt + +LOCAL_SRC_FILES := $(aaptSources) $(aaptMain) +LOCAL_C_INCLUDES += \ + $(aaptCIncludes) \ + bionic \ + external/stlport/stlport LOCAL_SHARED_LIBRARIES := \ - libandroidfw \ - libutils \ - libcutils \ - libpng \ - liblog \ - libz + libandroidfw \ + libutils \ + libcutils \ + libpng \ + liblog \ + libz LOCAL_STATIC_LIBRARIES := \ - libstlport_static \ - libexpat_static + libstlport_static \ + libexpat_static + +LOCAL_CPPFLAGS += -Wno-non-virtual-dtor include $(BUILD_EXECUTABLE) -endif + +endif # Not SDK_ONLY endif # No TARGET_BUILD_APPS or TARGET_BUILD_PDK diff --git a/tools/aapt/ApkBuilder.cpp b/tools/aapt/ApkBuilder.cpp new file mode 100644 index 0000000..12f6040 --- /dev/null +++ b/tools/aapt/ApkBuilder.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2014 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +#include "AaptAssets.h" +#include "ApkBuilder.h" + +using namespace android; + +ApkBuilder::ApkBuilder(const sp<WeakResourceFilter>& configFilter) + : mConfigFilter(configFilter) + , mDefaultFilter(new AndResourceFilter()) { + // Add the default split, which is present for all APKs. + mDefaultFilter->addFilter(mConfigFilter); + mSplits.add(new ApkSplit(std::set<ConfigDescription>(), mDefaultFilter, true)); +} + +status_t ApkBuilder::createSplitForConfigs(const std::set<ConfigDescription>& configs) { + const size_t N = mSplits.size(); + for (size_t i = 0; i < N; i++) { + const std::set<ConfigDescription>& splitConfigs = mSplits[i]->getConfigs(); + std::set<ConfigDescription>::const_iterator iter = configs.begin(); + for (; iter != configs.end(); iter++) { + if (splitConfigs.count(*iter) > 0) { + // Can't have overlapping configurations. + fprintf(stderr, "ERROR: Split configuration '%s' is already defined " + "in another split.\n", iter->toString().string()); + return ALREADY_EXISTS; + } + } + } + + sp<StrongResourceFilter> splitFilter = new StrongResourceFilter(configs); + + // Add the inverse filter of this split filter to the base apk filter so it will + // omit resources that belong in this split. + mDefaultFilter->addFilter(new InverseResourceFilter(splitFilter)); + + // Now add the apk-wide config filter to our split filter. + sp<AndResourceFilter> filter = new AndResourceFilter(); + filter->addFilter(splitFilter); + filter->addFilter(mConfigFilter); + mSplits.add(new ApkSplit(configs, filter)); + return NO_ERROR; +} + +status_t ApkBuilder::addEntry(const String8& path, const sp<AaptFile>& file) { + const size_t N = mSplits.size(); + for (size_t i = 0; i < N; i++) { + if (mSplits[i]->matches(file)) { + return mSplits.editItemAt(i)->addEntry(path, file); + } + } + // Entry can be dropped if it doesn't match any split. This will only happen + // if the enry doesn't mConfigFilter. + return NO_ERROR; +} + +void ApkBuilder::print() const { + fprintf(stderr, "APK Builder\n"); + fprintf(stderr, "-----------\n"); + const size_t N = mSplits.size(); + for (size_t i = 0; i < N; i++) { + mSplits[i]->print(); + fprintf(stderr, "\n"); + } +} + +ApkSplit::ApkSplit(const std::set<ConfigDescription>& configs, const sp<ResourceFilter>& filter, bool isBase) + : mConfigs(configs), mFilter(filter), mIsBase(isBase) { + std::set<ConfigDescription>::const_iterator iter = configs.begin(); + for (; iter != configs.end(); iter++) { + if (mName.size() > 0) { + mName.append(","); + mDirName.append("_"); + } + + String8 configStr = iter->toString(); + mName.append(configStr); + mDirName.append(configStr); + } +} + +status_t ApkSplit::addEntry(const String8& path, const sp<AaptFile>& file) { + if (!mFiles.insert(OutputEntry(path, file)).second) { + // Duplicate file. + return ALREADY_EXISTS; + } + return NO_ERROR; +} + +void ApkSplit::print() const { + fprintf(stderr, "APK Split '%s'\n", mName.string()); + + std::set<OutputEntry>::const_iterator iter = mFiles.begin(); + for (; iter != mFiles.end(); iter++) { + fprintf(stderr, " %s (%s)\n", iter->getPath().string(), iter->getFile()->getSourceFile().string()); + } +} diff --git a/tools/aapt/ApkBuilder.h b/tools/aapt/ApkBuilder.h new file mode 100644 index 0000000..db23c84 --- /dev/null +++ b/tools/aapt/ApkBuilder.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2014 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +#ifndef __APK_BUILDER_H +#define __APK_BUILDER_H + +#include <set> +#include <utils/Errors.h> +#include <utils/String8.h> +#include <utils/StrongPointer.h> +#include <utils/Vector.h> + +#include "ConfigDescription.h" +#include "OutputSet.h" +#include "ResourceFilter.h" + +class ApkSplit; +class AaptFile; + +class ApkBuilder : public android::RefBase { +public: + ApkBuilder(const sp<WeakResourceFilter>& configFilter); + + /** + * Tells the builder to generate a separate APK for resources that + * match the configurations specified. Split APKs can not have + * overlapping resources. + * + * NOTE: All splits should be set up before any files are added. + */ + android::status_t createSplitForConfigs(const std::set<ConfigDescription>& configs); + + /** + * Adds a file to be written to the final APK. It's name must not collide + * with that of any files previously added. When a Split APK is being + * generated, duplicates can exist as long as they are in different splits + * (resources.arsc, AndroidManifest.xml). + */ + android::status_t addEntry(const String8& path, const android::sp<AaptFile>& file); + + android::Vector<sp<ApkSplit> >& getSplits() { + return mSplits; + } + + android::sp<ApkSplit> getBaseSplit() { + return mSplits[0]; + } + + void print() const; + +private: + android::sp<ResourceFilter> mConfigFilter; + android::sp<AndResourceFilter> mDefaultFilter; + android::Vector<sp<ApkSplit> > mSplits; +}; + +class ApkSplit : public OutputSet { +public: + android::status_t addEntry(const String8& path, const android::sp<AaptFile>& file); + + const std::set<OutputEntry>& getEntries() const { + return mFiles; + } + + const std::set<ConfigDescription>& getConfigs() const { + return mConfigs; + } + + bool matches(const sp<AaptFile>& file) const { + return mFilter->match(file->getGroupEntry().toParams()); + } + + sp<ResourceFilter> getResourceFilter() const { + return mFilter; + } + + const android::String8& getPrintableName() const { + return mName; + } + + const android::String8& getDirectorySafeName() const { + return mDirName; + } + + bool isBase() const { + return mIsBase; + } + + void print() const; + +private: + friend class ApkBuilder; + + ApkSplit(const std::set<ConfigDescription>& configs, const android::sp<ResourceFilter>& filter, bool isBase=false); + + std::set<ConfigDescription> mConfigs; + const sp<ResourceFilter> mFilter; + const bool mIsBase; + String8 mName; + String8 mDirName; + std::set<OutputEntry> mFiles; +}; + +#endif // __APK_BUILDER_H diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h index 382cf5e..ceb52a0 100644 --- a/tools/aapt/Bundle.h +++ b/tools/aapt/Bundle.h @@ -60,7 +60,7 @@ public: mForce(false), mGrayscaleTolerance(0), mMakePackageDirs(false), mUpdate(false), mExtending(false), mRequireLocalization(false), mPseudolocalize(NO_PSEUDOLOCALIZATION), - mWantUTF16(false), mValues(false), + mWantUTF16(false), mValues(false), mIncludeMetaData(false), mCompressionMethod(0), mJunkPath(false), mOutputAPKFile(NULL), mManifestPackageNameOverride(NULL), mInstrumentationPackageNameOverride(NULL), mAutoAddOverlay(false), mGenDependencies(false), @@ -68,10 +68,12 @@ public: mAndroidManifestFile(NULL), mPublicOutputFile(NULL), mRClassDir(NULL), mResourceIntermediatesDir(NULL), mManifestMinSdkVersion(NULL), mMinSdkVersion(NULL), mTargetSdkVersion(NULL), mMaxSdkVersion(NULL), - mVersionCode(NULL), mVersionName(NULL), mCustomPackage(NULL), mExtraPackages(NULL), - mMaxResVersion(NULL), mDebugMode(false), mNonConstantId(false), mProduct(NULL), - mUseCrunchCache(false), mErrorOnFailedInsert(false), mOutputTextSymbols(NULL), + mVersionCode(NULL), mVersionName(NULL), mReplaceVersion(false), mCustomPackage(NULL), + mExtraPackages(NULL), mMaxResVersion(NULL), mDebugMode(false), mNonConstantId(false), + mProduct(NULL), mUseCrunchCache(false), mErrorOnFailedInsert(false), + mErrorOnMissingConfigEntry(false), mOutputTextSymbols(NULL), mSingleCrunchInputFile(NULL), mSingleCrunchOutputFile(NULL), + mBuildSharedLibrary(false), mArgc(0), mArgv(NULL) {} ~Bundle(void) {} @@ -107,6 +109,8 @@ public: void setWantUTF16(bool val) { mWantUTF16 = val; } bool getValues(void) const { return mValues; } void setValues(bool val) { mValues = val; } + bool getIncludeMetaData(void) const { return mIncludeMetaData; } + void setIncludeMetaData(bool val) { mIncludeMetaData = val; } int getCompressionMethod(void) const { return mCompressionMethod; } void setCompressionMethod(int val) { mCompressionMethod = val; } bool getJunkPath(void) const { return mJunkPath; } @@ -123,6 +127,8 @@ public: void setGenDependencies(bool val) { mGenDependencies = val; } bool getErrorOnFailedInsert() { return mErrorOnFailedInsert; } void setErrorOnFailedInsert(bool val) { mErrorOnFailedInsert = val; } + bool getErrorOnMissingConfigEntry() { return mErrorOnMissingConfigEntry; } + void setErrorOnMissingConfigEntry(bool val) { mErrorOnMissingConfigEntry = val; } bool getUTF16StringsOption() { return mWantUTF16 || !isMinSdkAtLeast(SDK_FROYO); @@ -145,10 +151,12 @@ public: void setPublicOutputFile(const char* file) { mPublicOutputFile = file; } const char* getRClassDir() const { return mRClassDir; } void setRClassDir(const char* dir) { mRClassDir = dir; } - const char* getConfigurations() const { return mConfigurations.size() > 0 ? mConfigurations.string() : NULL; } + const android::String8& getConfigurations() const { return mConfigurations; } 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 android::String8& getPreferredDensity() const { return mPreferredDensity; } + void setPreferredDensity(const char* val) { mPreferredDensity = val; } + void addSplitConfigurations(const char* val) { mPartialConfigurations.add(android::String8(val)); } + const android::Vector<android::String8>& getSplitConfigurations() const { return mPartialConfigurations; } const char* getResourceIntermediatesDir() const { return mResourceIntermediatesDir; } void setResourceIntermediatesDir(const char* dir) { mResourceIntermediatesDir = dir; } const android::Vector<const char*>& getPackageIncludes() const { return mPackageIncludes; } @@ -170,6 +178,8 @@ public: void setVersionCode(const char* val) { mVersionCode = val; } const char* getVersionName() const { return mVersionName; } void setVersionName(const char* val) { mVersionName = val; } + bool getReplaceVersion() { return mReplaceVersion; } + void setReplaceVersion(bool val) { mReplaceVersion = val; } const char* getCustomPackage() const { return mCustomPackage; } void setCustomPackage(const char* val) { mCustomPackage = val; } const char* getExtraPackages() const { return mExtraPackages; } @@ -190,6 +200,8 @@ public: void setSingleCrunchInputFile(const char* val) { mSingleCrunchInputFile = val; } const char* getSingleCrunchOutputFile() const { return mSingleCrunchOutputFile; } void setSingleCrunchOutputFile(const char* val) { mSingleCrunchOutputFile = val; } + bool getBuildSharedLibrary() const { return mBuildSharedLibrary; } + void setBuildSharedLibrary(bool val) { mBuildSharedLibrary = val; } /* * Set and get the file specification. @@ -261,6 +273,7 @@ private: short mPseudolocalize; bool mWantUTF16; bool mValues; + bool mIncludeMetaData; int mCompressionMethod; bool mJunkPath; const char* mOutputAPKFile; @@ -268,7 +281,6 @@ private: const char* mInstrumentationPackageNameOverride; bool mAutoAddOverlay; bool mGenDependencies; - const char* mAssetSourceDir; const char* mCrunchedOutputDir; const char* mProguardFile; const char* mAndroidManifestFile; @@ -276,7 +288,8 @@ private: const char* mRClassDir; const char* mResourceIntermediatesDir; android::String8 mConfigurations; - android::String8 mPreferredConfigurations; + android::String8 mPreferredDensity; + android::Vector<android::String8> mPartialConfigurations; android::Vector<const char*> mPackageIncludes; android::Vector<const char*> mJarFiles; android::Vector<const char*> mNoCompressExtensions; @@ -289,6 +302,7 @@ private: const char* mMaxSdkVersion; const char* mVersionCode; const char* mVersionName; + bool mReplaceVersion; const char* mCustomPackage; const char* mExtraPackages; const char* mMaxResVersion; @@ -297,9 +311,11 @@ private: const char* mProduct; bool mUseCrunchCache; bool mErrorOnFailedInsert; + bool mErrorOnMissingConfigEntry; const char* mOutputTextSymbols; const char* mSingleCrunchInputFile; const char* mSingleCrunchOutputFile; + bool mBuildSharedLibrary; /* file specification */ int mArgc; diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index 34292fe..4e0a9fe 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -3,6 +3,7 @@ // // Android Asset Packaging Tool main entry point. // +#include "ApkBuilder.h" #include "Main.h" #include "Bundle.h" #include "ResourceFilter.h" @@ -25,8 +26,9 @@ using namespace android; */ int doVersion(Bundle* bundle) { - if (bundle->getFileSpecCount() != 0) + if (bundle->getFileSpecCount() != 0) { printf("(ignoring extra arguments)\n"); + } printf("Android Asset Packaging Tool, v0.2\n"); return 0; @@ -46,13 +48,14 @@ ZipFile* openReadOnly(const char* fileName) zip = new ZipFile; result = zip->open(fileName, ZipFile::kOpenReadOnly); if (result != NO_ERROR) { - if (result == NAME_NOT_FOUND) + if (result == NAME_NOT_FOUND) { fprintf(stderr, "ERROR: '%s' not found\n", fileName); - else if (result == PERMISSION_DENIED) + } else if (result == PERMISSION_DENIED) { fprintf(stderr, "ERROR: '%s' access denied\n", fileName); - else + } else { fprintf(stderr, "ERROR: failed opening '%s' as Zip file\n", fileName); + } delete zip; return NULL; } @@ -73,8 +76,9 @@ ZipFile* openReadWrite(const char* fileName, bool okayToCreate) int flags; flags = ZipFile::kOpenReadWrite; - if (okayToCreate) + if (okayToCreate) { flags |= ZipFile::kOpenCreate; + } zip = new ZipFile; result = zip->open(fileName, flags); @@ -94,12 +98,13 @@ bail: */ const char* compressionName(int method) { - if (method == ZipEntry::kCompressStored) + if (method == ZipEntry::kCompressStored) { return "Stored"; - else if (method == ZipEntry::kCompressDeflated) + } else if (method == ZipEntry::kCompressDeflated) { return "Deflated"; - else + } else { return "Unknown"; + } } /* @@ -107,10 +112,11 @@ const char* compressionName(int method) */ int calcPercent(long uncompressedLen, long compressedLen) { - if (!uncompressedLen) + if (!uncompressedLen) { return 0; - else + } else { return (int) (100.0 - (compressedLen * 100.0) / uncompressedLen + 0.5); + } } /* @@ -135,8 +141,9 @@ int doList(Bundle* bundle) zipFileName = bundle->getFileSpecEntry(0); zip = openReadOnly(zipFileName); - if (zip == NULL) + if (zip == NULL) { goto bail; + } int count, i; @@ -248,7 +255,9 @@ String8 getAttribute(const ResXMLTree& tree, const char* ns, Res_value value; if (tree.getAttributeValue(idx, &value) != NO_ERROR) { if (value.dataType != Res_value::TYPE_STRING) { - if (outError != NULL) *outError = "attribute is not a string value"; + if (outError != NULL) { + *outError = "attribute is not a string value"; + } return String8(); } } @@ -266,7 +275,9 @@ static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* o Res_value value; if (tree.getAttributeValue(idx, &value) != NO_ERROR) { if (value.dataType != Res_value::TYPE_STRING) { - if (outError != NULL) *outError = "attribute is not a string value"; + if (outError != NULL) { + *outError = "attribute is not a string value"; + } return String8(); } } @@ -286,7 +297,9 @@ static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes, if (tree.getAttributeValue(idx, &value) != NO_ERROR) { 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"; + if (outError != NULL) { + *outError = "attribute is not an integer value"; + } return defValue; } } @@ -307,7 +320,9 @@ static int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXM } 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"; + if (outError != NULL) { + *outError = "attribute is not an integer value"; + } return defValue; } } @@ -330,7 +345,9 @@ static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& } resTable->resolveReference(&value, 0); if (value.dataType != Res_value::TYPE_STRING) { - if (outError != NULL) *outError = "attribute is not a string value"; + if (outError != NULL) { + *outError = "attribute is not a string value"; + } return String8(); } } @@ -340,6 +357,49 @@ static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& return str ? String8(str, len) : String8(); } +static void getResolvedResourceAttribute(Res_value* value, const ResTable* resTable, + const ResXMLTree& tree, uint32_t attrRes, String8* outError) +{ + ssize_t idx = indexOfAttribute(tree, attrRes); + if (idx < 0) { + if (outError != NULL) { + *outError = "attribute could not be found"; + } + return; + } + if (tree.getAttributeValue(idx, value) != NO_ERROR) { + if (value->dataType == Res_value::TYPE_REFERENCE) { + resTable->resolveReference(value, 0); + } + // The attribute was found and was resolved if need be. + return; + } + if (outError != NULL) { + *outError = "error getting resolved resource attribute"; + } +} + +static void printResolvedResourceAttribute(const ResTable* resTable, const ResXMLTree& tree, + uint32_t attrRes, String8 attrLabel, String8* outError) +{ + Res_value value; + getResolvedResourceAttribute(&value, resTable, tree, attrRes, outError); + if (*outError != "") { + *outError = "error print resolved resource attribute"; + return; + } + if (value.dataType == Res_value::TYPE_STRING) { + String8 result = getResolvedAttribute(resTable, tree, attrRes, outError); + printf("%s='%s'", attrLabel.string(), + ResTable::normalizeForOutput(result.string()).string()); + } else if (Res_value::TYPE_FIRST_INT <= value.dataType && + value.dataType <= Res_value::TYPE_LAST_INT) { + printf("%s='%d'", attrLabel.string(), value.data); + } else { + printf("%s='0x%x'", attrLabel.string(), (int)value.data); + } +} + // These are attribute resource constants for the platform, as found // in android.R.attr enum { @@ -349,6 +409,7 @@ enum { PERMISSION_ATTR = 0x01010006, RESOURCE_ATTR = 0x01010025, DEBUGGABLE_ATTR = 0x0101000f, + VALUE_ATTR = 0x01010024, VERSION_CODE_ATTR = 0x0101021b, VERSION_NAME_ATTR = 0x0101021c, SCREEN_ORIENTATION_ATTR = 0x0101001e, @@ -378,7 +439,7 @@ enum { BANNER_ATTR = 0x10103f2, }; -const char *getComponentName(String8 &pkgName, String8 &componentName) { +String8 getComponentName(String8 &pkgName, String8 &componentName) { ssize_t idx = componentName.find("."); String8 retStr(pkgName); if (idx == 0) { @@ -387,12 +448,12 @@ const char *getComponentName(String8 &pkgName, String8 &componentName) { retStr += "."; retStr += componentName; } else { - return componentName.string(); + return componentName; } - return retStr.string(); + return retStr; } -static void printCompatibleScreens(ResXMLTree& tree) { +static void printCompatibleScreens(ResXMLTree& tree, String8* outError) { size_t len; ResXMLTree::event_code_t code; int depth = 0; @@ -410,7 +471,12 @@ static void printCompatibleScreens(ResXMLTree& tree) { continue; } depth++; - String8 tag(tree.getElementName(&len)); + const char16_t* ctag16 = tree.getElementName(&len); + if (ctag16 == NULL) { + *outError = "failed to get XML element name (bad string pool)"; + return; + } + String8 tag(ctag16); if (tag == "screen") { int32_t screenSize = getIntegerAttribute(tree, SCREEN_SIZE_ATTR, NULL, -1); @@ -428,6 +494,29 @@ static void printCompatibleScreens(ResXMLTree& tree) { printf("\n"); } +static void printUsesPermission(const String8& name, bool optional=false, int maxSdkVersion=-1) { + printf("uses-permission: name='%s'", ResTable::normalizeForOutput(name.string()).string()); + if (maxSdkVersion != -1) { + printf(" maxSdkVersion='%d'", maxSdkVersion); + } + printf("\n"); + + if (optional) { + printf("optional-permission: name='%s'", + ResTable::normalizeForOutput(name.string()).string()); + if (maxSdkVersion != -1) { + printf(" maxSdkVersion='%d'", maxSdkVersion); + } + printf("\n"); + } +} + +static void printUsesImpliedPermission(const String8& name, const String8& reason) { + printf("uses-implied-permission: name='%s' reason='%s'\n", + ResTable::normalizeForOutput(name.string()).string(), + ResTable::normalizeForOutput(reason.string()).string()); +} + Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool offHost, String8 *outError = NULL) { @@ -452,7 +541,12 @@ Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { if (code == ResXMLTree::END_TAG) { depth--; - String8 tag(tree.getElementName(&len)); + const char16_t* ctag16 = tree.getElementName(&len); + if (ctag16 == NULL) { + *outError = "failed to get XML element name (bad string pool)"; + return Vector<String8>(); + } + String8 tag(ctag16); if (depth == 0 && tag == serviceTagName) { withinApduService = false; @@ -460,7 +554,12 @@ Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool } else if (code == ResXMLTree::START_TAG) { depth++; - String8 tag(tree.getElementName(&len)); + const char16_t* ctag16 = tree.getElementName(&len); + if (ctag16 == NULL) { + *outError = "failed to get XML element name (bad string pool)"; + return Vector<String8>(); + } + String8 tag(ctag16); if (depth == 1) { if (tag == serviceTagName) { @@ -627,7 +726,12 @@ int doDump(Bundle* bundle) continue; } depth++; - String8 tag(tree.getElementName(&len)); + const char16_t* ctag16 = tree.getElementName(&len); + if (ctag16 == NULL) { + fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n"); + goto bail; + } + String8 tag(ctag16); //printf("Depth %d tag %s\n", depth, tag.string()); if (depth == 1) { if (tag != "manifest") { @@ -635,7 +739,7 @@ int doDump(Bundle* bundle) goto bail; } String8 pkg = getAttribute(tree, NULL, "package", NULL); - printf("package: %s\n", pkg.string()); + printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string()); } else if (depth == 2 && tag == "permission") { String8 error; String8 name = getAttribute(tree, NAME_ATTR, &error); @@ -643,7 +747,8 @@ int doDump(Bundle* bundle) fprintf(stderr, "ERROR: %s\n", error.string()); goto bail; } - printf("permission: %s\n", name.string()); + printf("permission: %s\n", + ResTable::normalizeForOutput(name.string()).string()); } else if (depth == 2 && tag == "uses-permission") { String8 error; String8 name = getAttribute(tree, NAME_ATTR, &error); @@ -651,11 +756,9 @@ int doDump(Bundle* bundle) fprintf(stderr, "ERROR: %s\n", error.string()); goto bail; } - printf("uses-permission: %s\n", name.string()); - int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1); - if (!req) { - printf("optional-permission: %s\n", name.string()); - } + printUsesPermission(name, + getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0, + getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1)); } } } else if (strcmp("badging", option) == 0) { @@ -668,7 +771,9 @@ int doDump(Bundle* bundle) const size_t NC = configs.size(); for (size_t i=0; i<NC; i++) { int dens = configs[i].density; - if (dens == 0) dens = 160; + if (dens == 0) { + dens = 160; + } densities.add(dens); } @@ -802,7 +907,8 @@ int doDump(Bundle* bundle) printf("supports-input: '"); const size_t N = supportedInput.size(); for (size_t i=0; i<N; i++) { - printf("%s", supportedInput[i].string()); + printf("%s", ResTable::normalizeForOutput( + supportedInput[i].string()).string()); if (i != N - 1) { printf("' '"); } else { @@ -815,25 +921,27 @@ int doDump(Bundle* bundle) withinSupportsInput = false; } else if (depth < 3) { if (withinActivity && isMainActivity) { - const char *aName = getComponentName(pkg, activityName); + String8 aName(getComponentName(pkg, activityName)); if (isLauncherActivity) { printf("launchable-activity:"); - if (aName != NULL) { - printf(" name='%s' ", aName); + if (aName.length() > 0) { + printf(" name='%s' ", + ResTable::normalizeForOutput(aName.string()).string()); } printf(" label='%s' icon='%s'\n", - activityLabel.string(), - activityIcon.string()); + ResTable::normalizeForOutput(activityLabel.string()).string(), + ResTable::normalizeForOutput(activityIcon.string()).string()); } if (isLeanbackLauncherActivity) { printf("leanback-launchable-activity:"); - if (aName != NULL) { - printf(" name='%s' ", aName); + if (aName.length() > 0) { + printf(" name='%s' ", + ResTable::normalizeForOutput(aName.string()).string()); } printf(" label='%s' icon='%s' banner='%s'\n", - activityLabel.string(), - activityIcon.string(), - activityBanner.string()); + ResTable::normalizeForOutput(activityLabel.string()).string(), + ResTable::normalizeForOutput(activityIcon.string()).string(), + ResTable::normalizeForOutput(activityBanner.string()).string()); } } if (!hasIntentFilter) { @@ -882,7 +990,13 @@ int doDump(Bundle* bundle) continue; } depth++; - String8 tag(tree.getElementName(&len)); + + const char16_t* ctag16 = tree.getElementName(&len); + if (ctag16 == NULL) { + fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n"); + goto bail; + } + String8 tag(ctag16); //printf("Depth %d, %s\n", depth, tag.string()); if (depth == 1) { if (tag != "manifest") { @@ -890,7 +1004,8 @@ int doDump(Bundle* bundle) goto bail; } pkg = getAttribute(tree, NULL, "package", NULL); - printf("package: name='%s' ", pkg.string()); + printf("package: name='%s' ", + ResTable::normalizeForOutput(pkg.string()).string()); int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error); if (error != "") { fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string()); @@ -906,7 +1021,8 @@ int doDump(Bundle* bundle) fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string()); goto bail; } - printf("versionName='%s'\n", versionName.string()); + printf("versionName='%s'\n", + ResTable::normalizeForOutput(versionName.string()).string()); } else if (depth == 2) { withinApplication = false; if (tag == "application") { @@ -921,13 +1037,14 @@ int doDump(Bundle* bundle) if (llabel != "") { if (localeStr == NULL || strlen(localeStr) == 0) { label = llabel; - printf("application-label:'%s'\n", llabel.string()); + printf("application-label:'%s'\n", + ResTable::normalizeForOutput(llabel.string()).string()); } else { if (label == "") { label = llabel; } printf("application-label-%s:'%s'\n", localeStr, - llabel.string()); + ResTable::normalizeForOutput(llabel.string()).string()); } } } @@ -939,7 +1056,8 @@ int doDump(Bundle* bundle) assets.setConfiguration(tmpConfig); String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error); if (icon != "") { - printf("application-icon-%d:'%s'\n", densities[i], icon.string()); + printf("application-icon-%d:'%s'\n", densities[i], + ResTable::normalizeForOutput(icon.string()).string()); } } assets.setConfiguration(config); @@ -954,8 +1072,9 @@ int doDump(Bundle* bundle) 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()); + printf("application: label='%s' ", + ResTable::normalizeForOutput(label.string()).string()); + printf("icon='%s'\n", ResTable::normalizeForOutput(icon.string()).string()); if (testOnly != 0) { printf("testOnly='%d'\n", testOnly); } @@ -979,7 +1098,8 @@ int doDump(Bundle* bundle) goto bail; } if (name == "Donut") targetSdk = 4; - printf("sdkVersion:'%s'\n", name.string()); + printf("sdkVersion:'%s'\n", + ResTable::normalizeForOutput(name.string()).string()); } else if (code != -1) { targetSdk = code; printf("sdkVersion:'%d'\n", code); @@ -998,7 +1118,8 @@ int doDump(Bundle* bundle) goto bail; } if (name == "Donut" && targetSdk < 4) targetSdk = 4; - printf("targetSdkVersion:'%s'\n", name.string()); + printf("targetSdkVersion:'%s'\n", + ResTable::normalizeForOutput(name.string()).string()); } else if (code != -1) { if (targetSdk < code) { targetSdk = code; @@ -1103,7 +1224,8 @@ int doDump(Bundle* bundle) specScreenLandscapeFeature = true; } printf("uses-feature%s:'%s'\n", - req ? "" : "-not-required", name.string()); + req ? "" : "-not-required", + ResTable::normalizeForOutput(name.string()).string()); } else { int vers = getIntegerAttribute(tree, GL_ES_VERSION_ATTR, &error); @@ -1161,12 +1283,11 @@ int doDump(Bundle* bundle) } else if (name == "android.permission.WRITE_CALL_LOG") { hasWriteCallLogPermission = true; } - printf("uses-permission:'%s'\n", name.string()); - int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1); - if (!req) { - printf("optional-permission:'%s'\n", name.string()); - } - } else { + + printUsesPermission(name, + getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0, + getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1)); + } else { fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string()); goto bail; @@ -1174,7 +1295,8 @@ int doDump(Bundle* bundle) } else if (tag == "uses-package") { String8 name = getAttribute(tree, NAME_ATTR, &error); if (name != "" && error == "") { - printf("uses-package:'%s'\n", name.string()); + printf("uses-package:'%s'\n", + ResTable::normalizeForOutput(name.string()).string()); } else { fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string()); @@ -1183,7 +1305,8 @@ int doDump(Bundle* bundle) } else if (tag == "original-package") { String8 name = getAttribute(tree, NAME_ATTR, &error); if (name != "" && error == "") { - printf("original-package:'%s'\n", name.string()); + printf("original-package:'%s'\n", + ResTable::normalizeForOutput(name.string()).string()); } else { fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string()); @@ -1192,14 +1315,20 @@ int doDump(Bundle* bundle) } else if (tag == "supports-gl-texture") { String8 name = getAttribute(tree, NAME_ATTR, &error); if (name != "" && error == "") { - printf("supports-gl-texture:'%s'\n", name.string()); + printf("supports-gl-texture:'%s'\n", + ResTable::normalizeForOutput(name.string()).string()); } else { fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string()); goto bail; } } else if (tag == "compatible-screens") { - printCompatibleScreens(tree); + printCompatibleScreens(tree, &error); + if (error != "") { + fprintf(stderr, "ERROR getting compatible screens: %s\n", + error.string()); + goto bail; + } depth--; } else if (tag == "package-verifier") { String8 name = getAttribute(tree, NAME_ATTR, &error); @@ -1207,7 +1336,8 @@ int doDump(Bundle* bundle) String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error); if (publicKey != "" && error == "") { printf("package-verifier: name='%s' publicKey='%s'\n", - name.string(), publicKey.string()); + ResTable::normalizeForOutput(name.string()).string(), + ResTable::normalizeForOutput(publicKey.string()).string()); } } } @@ -1276,7 +1406,8 @@ int doDump(Bundle* bundle) int req = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1); printf("uses-library%s:'%s'\n", - req ? "" : "-not-required", libraryName.string()); + req ? "" : "-not-required", ResTable::normalizeForOutput( + libraryName.string()).string()); } else if (tag == "receiver") { withinReceiver = true; receiverName = getAttribute(tree, NAME_ATTR, &error); @@ -1302,8 +1433,8 @@ int doDump(Bundle* bundle) serviceName = getAttribute(tree, NAME_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:name' attribute for" - " service: %s\n", error.string()); + fprintf(stderr, "ERROR getting 'android:name' attribute for " + "service:%s\n", error.string()); goto bail; } @@ -1322,15 +1453,39 @@ int doDump(Bundle* bundle) fprintf(stderr, "ERROR getting 'android:permission' attribute for" " service '%s': %s\n", serviceName.string(), error.string()); } - } - } else if (withinSupportsInput && tag == "input-type") { - String8 name = getAttribute(tree, NAME_ATTR, &error); - if (name != "" && error == "") { - supportedInput.add(name); - } else { - fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", - error.string()); - goto bail; + } else if (bundle->getIncludeMetaData() && tag == "meta-data") { + String8 metaDataName = getResolvedAttribute(&res, tree, NAME_ATTR, &error); + if (error != "") { + fprintf(stderr, "ERROR getting 'android:name' attribute for " + "meta-data:%s\n", error.string()); + goto bail; + } + printf("meta-data: name='%s' ", + ResTable::normalizeForOutput(metaDataName.string()).string()); + printResolvedResourceAttribute(&res, tree, VALUE_ATTR, String8("value"), + &error); + if (error != "") { + // Try looking for a RESOURCE_ATTR + error = ""; + printResolvedResourceAttribute(&res, tree, RESOURCE_ATTR, + String8("resource"), &error); + if (error != "") { + fprintf(stderr, "ERROR getting 'android:value' or " + "'android:resource' attribute for " + "meta-data:%s\n", error.string()); + goto bail; + } + } + printf("\n"); + } else if (withinSupportsInput && tag == "input-type") { + String8 name = getAttribute(tree, NAME_ATTR, &error); + if (name != "" && error == "") { + supportedInput.add(name); + } else { + fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", + error.string()); + goto bail; + } } } } else if (depth == 4) { @@ -1387,14 +1542,16 @@ int doDump(Bundle* bundle) } } } - } else if ((depth == 5) && withinIntentFilter){ + } else if ((depth == 5) && withinIntentFilter) { String8 action; if (tag == "action") { action = getAttribute(tree, NAME_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string()); + fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", + error.string()); goto bail; } + if (withinActivity) { if (action == "android.intent.action.MAIN") { isMainActivity = true; @@ -1429,7 +1586,8 @@ int doDump(Bundle* bundle) if (tag == "category") { String8 category = getAttribute(tree, NAME_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string()); + fprintf(stderr, "ERROR getting 'name' attribute: %s\n", + error.string()); goto bail; } if (withinActivity) { @@ -1446,15 +1604,15 @@ int doDump(Bundle* bundle) // Pre-1.6 implicitly granted permission compatibility logic if (targetSdk < 4) { if (!hasWriteExternalStoragePermission) { - printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n"); - printf("uses-implied-permission:'android.permission.WRITE_EXTERNAL_STORAGE'," \ - "'targetSdkVersion < 4'\n"); + printUsesPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE")); + printUsesImpliedPermission(String8("android.permission.WRITE_EXTERNAL_STORAGE"), + String8("targetSdkVersion < 4")); hasWriteExternalStoragePermission = true; } if (!hasReadPhoneStatePermission) { - printf("uses-permission:'android.permission.READ_PHONE_STATE'\n"); - printf("uses-implied-permission:'android.permission.READ_PHONE_STATE'," \ - "'targetSdkVersion < 4'\n"); + printUsesPermission(String8("android.permission.READ_PHONE_STATE")); + printUsesImpliedPermission(String8("android.permission.READ_PHONE_STATE"), + String8("targetSdkVersion < 4")); } } @@ -1463,22 +1621,22 @@ int doDump(Bundle* bundle) // do this (regardless of target API version) because we can't have // an app with write permission but not read permission. if (!hasReadExternalStoragePermission && hasWriteExternalStoragePermission) { - printf("uses-permission:'android.permission.READ_EXTERNAL_STORAGE'\n"); - printf("uses-implied-permission:'android.permission.READ_EXTERNAL_STORAGE'," \ - "'requested WRITE_EXTERNAL_STORAGE'\n"); + printUsesPermission(String8("android.permission.READ_EXTERNAL_STORAGE")); + printUsesImpliedPermission(String8("android.permission.READ_EXTERNAL_STORAGE"), + String8("requested WRITE_EXTERNAL_STORAGE")); } // Pre-JellyBean call log permission compatibility. if (targetSdk < 16) { if (!hasReadCallLogPermission && hasReadContactsPermission) { - printf("uses-permission:'android.permission.READ_CALL_LOG'\n"); - printf("uses-implied-permission:'android.permission.READ_CALL_LOG'," \ - "'targetSdkVersion < 16 and requested READ_CONTACTS'\n"); + printUsesPermission(String8("android.permission.READ_CALL_LOG")); + printUsesImpliedPermission(String8("android.permission.READ_CALL_LOG"), + String8("targetSdkVersion < 16 and requested READ_CONTACTS")); } if (!hasWriteCallLogPermission && hasWriteContactsPermission) { - printf("uses-permission:'android.permission.WRITE_CALL_LOG'\n"); - printf("uses-implied-permission:'android.permission.WRITE_CALL_LOG'," \ - "'targetSdkVersion < 16 and requested WRITE_CONTACTS'\n"); + printUsesPermission(String8("android.permission.WRITE_CALL_LOG")); + printUsesImpliedPermission(String8("android.permission.WRITE_CALL_LOG"), + String8("targetSdkVersion < 16 and requested WRITE_CONTACTS")); } } @@ -1503,7 +1661,7 @@ int doDump(Bundle* bundle) printf("uses-implied-feature:'android.hardware.camera'," \ "'requested android.hardware.camera.autofocus feature'\n"); } else if (hasCameraPermission) { - // if app wants to use camera but didn't request the feature, we infer + // if app wants to use camera but didn't request the feature, we infer // that it meant to, and further that it wants autofocus // (which was the 1.0 - 1.5 behavior) printf("uses-feature:'android.hardware.camera'\n"); @@ -1656,7 +1814,9 @@ int doDump(Bundle* bundle) if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0 && requiresSmallestWidthDp > 0) { int compatWidth = compatibleWidthLimitDp; - if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp; + if (compatWidth <= 0) { + compatWidth = requiresSmallestWidthDp; + } if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) { smallScreen = -1; } else { @@ -1701,10 +1861,18 @@ int doDump(Bundle* bundle) || compatibleWidthLimitDp > 0) ? -1 : 0; } printf("supports-screens:"); - if (smallScreen != 0) printf(" 'small'"); - if (normalScreen != 0) printf(" 'normal'"); - if (largeScreen != 0) printf(" 'large'"); - if (xlargeScreen != 0) printf(" 'xlarge'"); + if (smallScreen != 0) { + printf(" 'small'"); + } + if (normalScreen != 0) { + printf(" 'normal'"); + } + if (largeScreen != 0) { + printf(" 'large'"); + } + if (xlargeScreen != 0) { + printf(" 'xlarge'"); + } printf("\n"); printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false"); if (requiresSmallestWidthDp > 0) { @@ -1740,7 +1908,8 @@ int doDump(Bundle* bundle) if (dir->getFileCount() > 0) { printf("native-code:"); for (size_t i=0; i<dir->getFileCount(); i++) { - printf(" '%s'", dir->getFileName(i).string()); + printf(" '%s'", ResTable::normalizeForOutput( + dir->getFileName(i).string()).string()); } printf("\n"); } @@ -1813,7 +1982,8 @@ int doAdd(Bundle* bundle) } else { if (bundle->getJunkPath()) { String8 storageName = String8(fileName).getPathLeaf(); - printf(" '%s' as '%s'...\n", fileName, storageName.string()); + printf(" '%s' as '%s'...\n", fileName, + ResTable::normalizeForOutput(storageName.string()).string()); result = zip->add(fileName, storageName.string(), bundle->getCompressionMethod(), NULL); } else { @@ -1823,12 +1993,13 @@ int doAdd(Bundle* bundle) } if (result != NO_ERROR) { fprintf(stderr, "Unable to add '%s' to '%s'", bundle->getFileSpecEntry(i), zipFileName); - if (result == NAME_NOT_FOUND) + if (result == NAME_NOT_FOUND) { fprintf(stderr, ": file not found\n"); - else if (result == ALREADY_EXISTS) + } else if (result == ALREADY_EXISTS) { fprintf(stderr, ": already exists in archive\n"); - else + } else { fprintf(stderr, "\n"); + } goto bail; } } @@ -1895,6 +2066,58 @@ bail: return (result != NO_ERROR); } +static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder, bool ignoreConfig=false) { + const size_t numDirs = dir->getDirs().size(); + for (size_t i = 0; i < numDirs; i++) { + bool ignore = ignoreConfig; + const sp<AaptDir>& subDir = dir->getDirs().valueAt(i); + const char* dirStr = subDir->getLeaf().string(); + if (!ignore && strstr(dirStr, "mipmap") == dirStr) { + ignore = true; + } + status_t err = addResourcesToBuilder(subDir, builder, ignore); + if (err != NO_ERROR) { + return err; + } + } + + const size_t numFiles = dir->getFiles().size(); + for (size_t i = 0; i < numFiles; i++) { + sp<AaptGroup> gp = dir->getFiles().valueAt(i); + const size_t numConfigs = gp->getFiles().size(); + for (size_t j = 0; j < numConfigs; j++) { + status_t err = NO_ERROR; + if (ignoreConfig) { + err = builder->getBaseSplit()->addEntry(gp->getPath(), gp->getFiles().valueAt(j)); + } else { + err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j)); + } + if (err != NO_ERROR) { + fprintf(stderr, "Failed to add %s (%s) to builder.\n", + gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string()); + return err; + } + } + } + return NO_ERROR; +} + +static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) { + if (split->isBase()) { + return original; + } + + String8 ext(original.getPathExtension()); + if (ext == String8(".apk")) { + return String8::format("%s_%s%s", + original.getBasePath().string(), + split->getDirectorySafeName().string(), + ext.string()); + } + + return String8::format("%s_%s", original.string(), + split->getDirectorySafeName().string()); +} /* * Package up an asset directory and associated application files. @@ -1908,17 +2131,18 @@ int doPackage(Bundle* bundle) int N; FILE* fp; String8 dependencyFile; + sp<ApkBuilder> builder; // -c en_XA or/and ar_XB means do pseudolocalization - ResourceFilter filter; - err = filter.parse(bundle->getConfigurations()); + sp<WeakResourceFilter> configFilter = new WeakResourceFilter(); + err = configFilter->parse(bundle->getConfigurations()); if (err != NO_ERROR) { goto bail; } - if (filter.containsPseudo()) { + if (configFilter->containsPseudo()) { bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED); } - if (filter.containsPseudoBidi()) { + if (configFilter->containsPseudoBidi()) { bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI); } @@ -1966,9 +2190,32 @@ int doPackage(Bundle* bundle) assets->print(String8()); } + // Create the ApkBuilder, which will collect the compiled files + // to write to the final APK (or sets of APKs if we are building + // a Split APK. + builder = new ApkBuilder(configFilter); + + // If we are generating a Split APK, find out which configurations to split on. + if (bundle->getSplitConfigurations().size() > 0) { + const Vector<String8>& splitStrs = bundle->getSplitConfigurations(); + const size_t numSplits = splitStrs.size(); + for (size_t i = 0; i < numSplits; i++) { + std::set<ConfigDescription> configs; + if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) { + fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string()); + goto bail; + } + + err = builder->createSplitForConfigs(configs); + if (err != NO_ERROR) { + goto bail; + } + } + } + // If they asked for any fileAs that need to be compiled, do so. if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) { - err = buildResources(bundle, assets); + err = buildResources(bundle, assets, builder); if (err != 0) { goto bail; } @@ -2055,11 +2302,24 @@ int doPackage(Bundle* bundle) // Write the apk if (outputAPKFile) { - err = writeAPK(bundle, assets, String8(outputAPKFile)); + // Gather all resources and add them to the APK Builder. The builder will then + // figure out which Split they belong in. + err = addResourcesToBuilder(assets, builder); if (err != NO_ERROR) { - fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile); goto bail; } + + const Vector<sp<ApkSplit> >& splits = builder->getSplits(); + const size_t numSplits = splits.size(); + for (size_t i = 0; i < numSplits; i++) { + const sp<ApkSplit>& split = splits[i]; + String8 outputPath = buildApkName(String8(outputAPKFile), split); + err = writeAPK(bundle, outputPath, split); + if (err != NO_ERROR) { + fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string()); + goto bail; + } + } } // If we've been asked to generate a dependency file, we need to finish up here. @@ -2096,7 +2356,7 @@ bail: * * POSTCONDITIONS * Destination directory will be updated to match the PNG files in - * the source directory. + * the source directory. */ int doCrunch(Bundle* bundle) { diff --git a/tools/aapt/ConfigDescription.h b/tools/aapt/ConfigDescription.h new file mode 100644 index 0000000..779c423 --- /dev/null +++ b/tools/aapt/ConfigDescription.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2014 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +#ifndef __CONFIG_DESCRIPTION_H +#define __CONFIG_DESCRIPTION_H + +#include <androidfw/ResourceTypes.h> + +/** + * Subclass of ResTable_config that adds convenient + * initialization and comparison methods. + */ +struct ConfigDescription : public android::ResTable_config { + ConfigDescription() { + memset(this, 0, sizeof(*this)); + size = sizeof(android::ResTable_config); + } + ConfigDescription(const android::ResTable_config&o) { + *static_cast<android::ResTable_config*>(this) = o; + size = sizeof(android::ResTable_config); + } + ConfigDescription(const ConfigDescription&o) { + *static_cast<android::ResTable_config*>(this) = o; + } + + ConfigDescription& operator=(const android::ResTable_config& o) { + *static_cast<android::ResTable_config*>(this) = o; + size = sizeof(android::ResTable_config); + return *this; + } + ConfigDescription& operator=(const ConfigDescription& o) { + *static_cast<android::ResTable_config*>(this) = o; + return *this; + } + + inline bool operator<(const ConfigDescription& o) const { return compare(o) < 0; } + inline bool operator<=(const ConfigDescription& o) const { return compare(o) <= 0; } + inline bool operator==(const ConfigDescription& o) const { return compare(o) == 0; } + inline bool operator!=(const ConfigDescription& o) const { return compare(o) != 0; } + inline bool operator>=(const ConfigDescription& o) const { return compare(o) >= 0; } + inline bool operator>(const ConfigDescription& o) const { return compare(o) > 0; } +}; + +#endif // __CONFIG_DESCRIPTION_H diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp index db74831..12f5b92 100644 --- a/tools/aapt/Images.cpp +++ b/tools/aapt/Images.cpp @@ -81,6 +81,12 @@ struct image_info png_bytepp allocRows; }; +static void log_warning(png_structp png_ptr, png_const_charp warning_message) +{ + const char* imageName = (const char*) png_get_error_ptr(png_ptr); + fprintf(stderr, "%s: libpng warning: %s\n", imageName, warning_message); +} + static void read_png(const char* imageName, png_structp read_ptr, png_infop read_info, image_info* outImageInfo) @@ -89,6 +95,8 @@ static void read_png(const char* imageName, int bit_depth, interlace_type, compression_type; int i; + png_set_error_fn(read_ptr, const_cast<char*>(imageName), + NULL /* use default errorfn */, log_warning); png_read_info(read_ptr, read_info); png_get_IHDR(read_ptr, read_info, &outImageInfo->width, @@ -119,6 +127,8 @@ static void read_png(const char* imageName, if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(read_ptr); + png_set_interlace_handling(read_ptr); + png_read_update_info(read_ptr, read_info); outImageInfo->rows = (png_bytepp)malloc( @@ -931,7 +941,7 @@ static void analyze_image(const char *imageName, image_info &imageInfo, int gray gg = *row++; bb = *row++; aa = *row++; - + if (isGrayscale) { *out++ = rr; } else { diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp index d1d3deb..5a60014 100644 --- a/tools/aapt/Main.cpp +++ b/tools/aapt/Main.cpp @@ -47,7 +47,7 @@ void usage(void) " %s l[ist] [-v] [-a] file.{zip,jar,apk}\n" " List contents of Zip-compatible archive.\n\n", gProgName); fprintf(stderr, - " %s d[ump] [--values] WHAT file.{apk} [asset [asset ...]]\n" + " %s d[ump] [--values] [--include-meta-data] WHAT file.{apk} [asset [asset ...]]\n" " strings Print the contents of the resource table string pool in the APK.\n" " badging Print the label and icon for the app declared in APK.\n" " permissions Print the permissions from the APK.\n" @@ -70,6 +70,7 @@ void usage(void) " [-F apk-file] [-J R-file-dir] \\\n" " [--product product1,product2,...] \\\n" " [-c CONFIGS] [--preferred-configurations CONFIGS] \\\n" + " [--split CONFIGS [--split CONFIGS]] \\\n" " [raw-files-dir [raw-files-dir] ...] \\\n" " [--output-text-symbols DIR]\n" "\n" @@ -138,6 +139,8 @@ void usage(void) " --debug-mode\n" " inserts android:debuggable=\"true\" in to the application node of the\n" " manifest, making the application debuggable even on production devices.\n" + " --include-meta-data\n" + " when used with \"dump badging\" also includes meta-data tags.\n" " --min-sdk-version\n" " inserts android:minSdkVersion in to manifest. If the version is 7 or\n" " higher, the default encoding for resources will be in UTF-8.\n" @@ -151,6 +154,11 @@ void usage(void) " inserts android:versionCode in to manifest.\n" " --version-name\n" " inserts android:versionName in to manifest.\n" + " --replace-version\n" + " If --version-code and/or --version-name are specified, these\n" + " values will replace any value already in the manifest. By\n" + " default, nothing is changed if the manifest already defines\n" + " these attributes.\n" " --custom-package\n" " generates R.java into a different package.\n" " --extra-packages\n" @@ -159,10 +167,12 @@ void usage(void) " 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" + " --preferred-density\n" + " Specifies a preference for a particular density. Resources that do not\n" + " match this density and have variants that are a closer match are removed.\n" + " --split\n" + " Builds a separate split APK for the configurations listed. This can\n" + " be loaded alongside the base APK at runtime.\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" @@ -183,11 +193,16 @@ void usage(void) " Make the resources ID non constant. This is required to make an R java class\n" " that does not contain the final value but is used to make reusable compiled\n" " libraries that need to access resources.\n" + " --shared-lib\n" + " Make a shared library resource package that can be loaded by an application\n" + " at runtime to access the libraries resources. Implies --non-constant-id.\n" " --error-on-failed-insert\n" " Forces aapt to return an error if it fails to insert values into the manifest\n" " with --debug-mode, --min-sdk-version, --target-sdk-version --version-code\n" " and --version-name.\n" " Insertion typically fails if the manifest already defines the attribute.\n" + " --error-on-missing-config-entry\n" + " Forces aapt to return an error if it fails to find an entry for a configuration.\n" " --output-text-symbols\n" " Generates a text file containing the resource symbols of the R class in the\n" " specified folder.\n" @@ -528,8 +543,12 @@ int main(int argc, char* const argv[]) goto bail; } bundle.setVersionName(argv[0]); + } else if (strcmp(cp, "-replace-version") == 0) { + bundle.setReplaceVersion(true); } else if (strcmp(cp, "-values") == 0) { bundle.setValues(true); + } else if (strcmp(cp, "-include-meta-data") == 0) { + bundle.setIncludeMetaData(true); } else if (strcmp(cp, "-custom-package") == 0) { argc--; argv++; @@ -552,15 +571,24 @@ int main(int argc, char* const argv[]) bundle.setGenDependencies(true); } else if (strcmp(cp, "-utf16") == 0) { bundle.setWantUTF16(true); - } else if (strcmp(cp, "-preferred-configurations") == 0) { + } else if (strcmp(cp, "-preferred-density") == 0) { argc--; argv++; if (!argc) { - fprintf(stderr, "ERROR: No argument supplied for '--preferred-configurations' option\n"); + fprintf(stderr, "ERROR: No argument supplied for '--preferred-density' option\n"); wantUsage = true; goto bail; } - bundle.addPreferredConfigurations(argv[0]); + bundle.setPreferredDensity(argv[0]); + } else if (strcmp(cp, "-split") == 0) { + argc--; + argv++; + if (!argc) { + fprintf(stderr, "ERROR: No argument supplied for '--split' option\n"); + wantUsage = true; + goto bail; + } + bundle.addSplitConfigurations(argv[0]); } else if (strcmp(cp, "-rename-manifest-package") == 0) { argc--; argv++; @@ -583,6 +611,8 @@ int main(int argc, char* const argv[]) bundle.setAutoAddOverlay(true); } else if (strcmp(cp, "-error-on-failed-insert") == 0) { bundle.setErrorOnFailedInsert(true); + } else if (strcmp(cp, "-error-on-missing-config-entry") == 0) { + bundle.setErrorOnMissingConfigEntry(true); } else if (strcmp(cp, "-output-text-symbols") == 0) { argc--; argv++; @@ -603,6 +633,9 @@ int main(int argc, char* const argv[]) bundle.setProduct(argv[0]); } else if (strcmp(cp, "-non-constant-id") == 0) { bundle.setNonConstantId(true); + } else if (strcmp(cp, "-shared-lib") == 0) { + bundle.setNonConstantId(true); + bundle.setBuildSharedLibrary(true); } else if (strcmp(cp, "-no-crunch") == 0) { bundle.setUseCrunchCache(true); } else if (strcmp(cp, "-ignore-assets") == 0) { diff --git a/tools/aapt/Main.h b/tools/aapt/Main.h index a6b39ac..34c4496 100644 --- a/tools/aapt/Main.h +++ b/tools/aapt/Main.h @@ -10,8 +10,12 @@ #include <utils/threads.h> #include <utils/List.h> #include <utils/Errors.h> -#include "Bundle.h" +#include <utils/StrongPointer.h> + #include "AaptAssets.h" +#include "ApkBuilder.h" +#include "Bundle.h" +#include "ResourceFilter.h" #include "ZipFile.h" @@ -22,6 +26,8 @@ #include <time.h> #endif /* BENCHMARK */ +class OutputSet; + extern int doVersion(Bundle* bundle); extern int doList(Bundle* bundle); extern int doDump(Bundle* bundle); @@ -34,13 +40,13 @@ extern int doSingleCrunch(Bundle* bundle); extern int calcPercent(long uncompressedLen, long compressedLen); extern android::status_t writeAPK(Bundle* bundle, - const sp<AaptAssets>& assets, - const android::String8& outputFile); + const android::String8& outputFile, + const android::sp<OutputSet>& outputSet); extern android::status_t updatePreProcessedCache(Bundle* bundle); extern android::status_t buildResources(Bundle* bundle, - const sp<AaptAssets>& assets); + const sp<AaptAssets>& assets, sp<ApkBuilder>& builder); extern android::status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets, const String8& pkgName, bool includePrivate); @@ -49,8 +55,6 @@ extern android::status_t writeProguardFile(Bundle* bundle, const sp<AaptAssets>& extern bool isValidResourceType(const String8& type); -ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptAssets>& assets); - extern status_t filterResources(Bundle* bundle, const sp<AaptAssets>& assets); int dumpResources(Bundle* bundle); diff --git a/tools/aapt/OutputSet.h b/tools/aapt/OutputSet.h new file mode 100644 index 0000000..ea9ef70 --- /dev/null +++ b/tools/aapt/OutputSet.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2014 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +#ifndef __OUTPUT_SET_H +#define __OUTPUT_SET_H + +#include <set> +#include <utils/Errors.h> +#include <utils/String8.h> +#include <utils/StrongPointer.h> + +class AaptFile; + +class OutputEntry { +public: + OutputEntry() {} + OutputEntry(const android::String8& path, const android::sp<const AaptFile>& file) + : mPath(path), mFile(file) {} + + inline const android::sp<const AaptFile>& getFile() const { + return mFile; + } + + inline const android::String8& getPath() const { + return mPath; + } + + bool operator<(const OutputEntry& o) const { return getPath() < o.mPath; } + bool operator==(const OutputEntry& o) const { return getPath() == o.mPath; } + +private: + android::String8 mPath; + android::sp<const AaptFile> mFile; +}; + +class OutputSet : public virtual android::RefBase { +public: + virtual const std::set<OutputEntry>& getEntries() const = 0; + + virtual ~OutputSet() {} +}; + +#endif // __OUTPUT_SET_H diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp index 872d95c..dc16e35 100644 --- a/tools/aapt/Package.cpp +++ b/tools/aapt/Package.cpp @@ -5,6 +5,7 @@ // #include "Main.h" #include "AaptAssets.h" +#include "OutputSet.h" #include "ResourceTable.h" #include "ResourceFilter.h" @@ -36,11 +37,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, const ResourceFilter* filter); -bool processFile(Bundle* bundle, ZipFile* zip, - const sp<AaptGroup>& group, const sp<AaptFile>& file); +ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<const OutputSet>& outputSet); +bool processFile(Bundle* bundle, ZipFile* zip, String8 storageName, const sp<const AaptFile>& file); bool okayToCompress(Bundle* bundle, const String8& pathName); ssize_t processJarFiles(Bundle* bundle, ZipFile* zip); @@ -51,8 +49,7 @@ ssize_t processJarFiles(Bundle* bundle, ZipFile* zip); * On success, "bundle->numPackages" will be the number of Zip packages * we created. */ -status_t writeAPK(Bundle* bundle, const sp<AaptAssets>& assets, - const String8& outputFile) +status_t writeAPK(Bundle* bundle, const String8& outputFile, const sp<OutputSet>& outputSet) { #if BENCHMARK fprintf(stdout, "BENCHMARK: Starting APK Bundling \n"); @@ -112,7 +109,7 @@ status_t writeAPK(Bundle* bundle, const sp<AaptAssets>& assets, printf("Writing all files...\n"); } - count = processAssets(bundle, zip, assets); + count = processAssets(bundle, zip, outputSet); if (count < 0) { fprintf(stderr, "ERROR: unable to process assets while packaging '%s'\n", outputFile.string()); @@ -218,72 +215,24 @@ bail: return result; } -ssize_t processAssets(Bundle* bundle, ZipFile* zip, - const sp<AaptAssets>& assets) -{ - ResourceFilter filter; - status_t status = filter.parse(bundle->getConfigurations()); - if (status != NO_ERROR) { - return -1; - } - - ssize_t count = 0; - - const size_t N = assets->getGroupEntries().size(); - for (size_t i=0; i<N; i++) { - const AaptGroupEntry& ge = assets->getGroupEntries()[i]; - - 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, const ResourceFilter* filter) +ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<const OutputSet>& outputSet) { ssize_t count = 0; - - const size_t ND = dir->getDirs().size(); - size_t i; - for (i=0; i<ND; i++) { - 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); - ssize_t fi = gp->getFiles().indexOfKey(ge); - if (fi >= 0) { - sp<AaptFile> fl = gp->getFiles().valueAt(fi); - if (!processFile(bundle, zip, gp, fl)) { + const std::set<OutputEntry>& entries = outputSet->getEntries(); + std::set<OutputEntry>::const_iterator iter = entries.begin(); + for (; iter != entries.end(); iter++) { + const OutputEntry& entry = *iter; + if (entry.getFile() == NULL) { + fprintf(stderr, "warning: null file being processed.\n"); + } else { + String8 storagePath(entry.getPath()); + storagePath.convertToResPath(); + if (!processFile(bundle, zip, storagePath, entry.getFile())) { return UNKNOWN_ERROR; } count++; } } - return count; } @@ -294,12 +243,10 @@ ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptDir>& dir, * delete the existing entry before adding the new one. */ bool processFile(Bundle* bundle, ZipFile* zip, - const sp<AaptGroup>& group, const sp<AaptFile>& file) + String8 storageName, const sp<const AaptFile>& file) { const bool hasData = file->hasData(); - String8 storageName(group->getPath()); - storageName.convertToResPath(); ZipEntry* entry; bool fromGzip = false; status_t result; @@ -403,8 +350,8 @@ bool processFile(Bundle* bundle, ZipFile* zip, fprintf(stderr, " Unable to add '%s': file already in archive (try '-u'?)\n", file->getPrintableSource().string()); } else { - fprintf(stderr, " Unable to add '%s': Zip add failed\n", - file->getPrintableSource().string()); + fprintf(stderr, " Unable to add '%s': Zip add failed (%d)\n", + file->getPrintableSource().string(), result); } return false; } diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index 42b1905..e599643 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -179,24 +179,6 @@ bool isValidResourceType(const String8& type) || type == "color" || type == "menu" || type == "mipmap"; } -static sp<AaptFile> getResourceFile(const sp<AaptAssets>& assets, bool makeIfNecessary=true) -{ - sp<AaptGroup> group = assets->getFiles().valueFor(String8("resources.arsc")); - sp<AaptFile> file; - if (group != NULL) { - file = group->getFiles().valueFor(AaptGroupEntry()); - if (file != NULL) { - return file; - } - } - - if (!makeIfNecessary) { - return NULL; - } - return assets->addFile(String8("resources.arsc"), AaptGroupEntry(), String8(), - NULL, String8()); -} - static status_t parsePackage(Bundle* bundle, const sp<AaptAssets>& assets, const sp<AaptGroup>& grp) { @@ -359,23 +341,6 @@ static status_t preProcessImages(const Bundle* bundle, const sp<AaptAssets>& ass return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR; } -status_t postProcessImages(const sp<AaptAssets>& assets, - ResourceTable* table, - const sp<ResourceTypeSet>& set) -{ - ResourceDirIterator it(set, String8("drawable")); - bool hasErrors = false; - ssize_t res; - while ((res=it.next()) == NO_ERROR) { - res = postProcessImage(assets, table, it.getFile()); - if (res < NO_ERROR) { - hasErrors = true; - } - } - - return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR; -} - static void collect_files(const sp<AaptDir>& dir, KeyedVector<String8, sp<ResourceTypeSet> >* resources) { @@ -470,7 +435,7 @@ static int validateAttr(const String8& path, const ResTable& table, value.data); return ATTR_NOT_FOUND; } - + pool = table.getTableStringBlock(strIdx); #if 0 if (pool != NULL) { @@ -676,13 +641,15 @@ static bool applyFileOverlay(Bundle *bundle, } /* - * Inserts an attribute in a given node, only if the attribute does not - * exist. + * Inserts an attribute in a given node. * If errorOnFailedInsert is true, and the attribute already exists, returns false. - * Returns true otherwise, even if the attribute already exists. + * If replaceExisting is true, the attribute will be updated if it already exists. + * Returns true otherwise, even if the attribute already exists, and does not modify + * the existing attribute's value. */ bool addTagAttribute(const sp<XMLNode>& node, const char* ns8, - const char* attr8, const char* value, bool errorOnFailedInsert) + const char* attr8, const char* value, bool errorOnFailedInsert, + bool replaceExisting) { if (value == NULL) { return true; @@ -691,7 +658,16 @@ bool addTagAttribute(const sp<XMLNode>& node, const char* ns8, const String16 ns(ns8); const String16 attr(attr8); - if (node->getAttribute(ns, attr) != NULL) { + XMLNode::attribute_entry* existingEntry = node->editAttribute(ns, attr); + if (existingEntry != NULL) { + if (replaceExisting) { + NOISY(printf("Info: AndroidManifest.xml already defines %s (in %s);" + " overwriting existing value from manifest.\n", + String8(attr).string(), String8(ns).string())); + existingEntry->string = String16(value); + return true; + } + if (errorOnFailedInsert) { fprintf(stderr, "Error: AndroidManifest.xml already defines %s (in %s);" " cannot insert new value %s.\n", @@ -706,11 +682,23 @@ bool addTagAttribute(const sp<XMLNode>& node, const char* ns8, // don't stop the build. return true; } - + node->addAttribute(ns, attr, String16(value)); return true; } +/* + * Inserts an attribute in a given node, only if the attribute does not + * exist. + * If errorOnFailedInsert is true, and the attribute already exists, returns false. + * Returns true otherwise, even if the attribute already exists. + */ +bool addTagAttribute(const sp<XMLNode>& node, const char* ns8, + const char* attr8, const char* value, bool errorOnFailedInsert) +{ + return addTagAttribute(node, ns8, attr8, value, errorOnFailedInsert, false); +} + static void fullyQualifyClassName(const String8& package, sp<XMLNode> node, const String16& attrName) { XMLNode::attribute_entry* attr = node->editAttribute( @@ -748,16 +736,17 @@ status_t massageManifest(Bundle* bundle, sp<XMLNode> root) } bool errorOnFailedInsert = bundle->getErrorOnFailedInsert(); + bool replaceVersion = bundle->getReplaceVersion(); if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionCode", - bundle->getVersionCode(), errorOnFailedInsert)) { + bundle->getVersionCode(), errorOnFailedInsert, replaceVersion)) { return UNKNOWN_ERROR; } if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionName", - bundle->getVersionName(), errorOnFailedInsert)) { + bundle->getVersionName(), errorOnFailedInsert, replaceVersion)) { return UNKNOWN_ERROR; } - + if (bundle->getMinSdkVersion() != NULL || bundle->getTargetSdkVersion() != NULL || bundle->getMaxSdkVersion() != NULL) { @@ -766,7 +755,7 @@ status_t massageManifest(Bundle* bundle, sp<XMLNode> root) vers = XMLNode::newElement(root->getFilename(), String16(), String16("uses-sdk")); root->insertChildAt(vers, 0); } - + if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "minSdkVersion", bundle->getMinSdkVersion(), errorOnFailedInsert)) { return UNKNOWN_ERROR; @@ -841,7 +830,7 @@ status_t massageManifest(Bundle* bundle, sp<XMLNode> root) } } } - + return NO_ERROR; } @@ -882,7 +871,38 @@ status_t updatePreProcessedCache(Bundle* bundle) return 0; } -status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) +status_t generateAndroidManifestForSplit(const String16& package, const sp<ApkSplit>& split, + sp<AaptFile>& outFile) { + const String8 filename("AndroidManifest.xml"); + const String16 androidPrefix("android"); + const String16 androidNSUri("http://schemas.android.com/apk/res/android"); + sp<XMLNode> root = XMLNode::newNamespace(filename, androidPrefix, androidNSUri); + + // Build the <manifest> tag + sp<XMLNode> manifest = XMLNode::newElement(filename, String16(), String16("manifest")); + + // Add the 'package' attribute which is set to the original package name. + manifest->addAttribute(String16(), String16("package"), package); + + // Add the 'split' attribute which describes the configurations included. + String8 splitName("config_"); + splitName.append(split->getDirectorySafeName()); + manifest->addAttribute(String16(), String16("split"), String16(splitName)); + + // Build an empty <application> tag (required). + sp<XMLNode> app = XMLNode::newElement(filename, String16(), String16("application")); + manifest->addChild(app); + root->addChild(manifest); + + status_t err = root->flatten(outFile, true, true); + if (err != NO_ERROR) { + return err; + } + outFile->setCompressionMethod(ZipEntry::kCompressDeflated); + return NO_ERROR; +} + +status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuilder>& builder) { // First, look for a package file to parse. This is required to // be able to generate the resource information. @@ -925,7 +945,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) // -------------------------------------------------------------- // resType -> leafName -> group - KeyedVector<String8, sp<ResourceTypeSet> > *resources = + KeyedVector<String8, sp<ResourceTypeSet> > *resources = new KeyedVector<String8, sp<ResourceTypeSet> >; collect_files(assets, resources); @@ -957,7 +977,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) // now go through any resource overlays and collect their files sp<AaptAssets> current = assets->getOverlay(); while(current.get()) { - KeyedVector<String8, sp<ResourceTypeSet> > *resources = + KeyedVector<String8, sp<ResourceTypeSet> > *resources = new KeyedVector<String8, sp<ResourceTypeSet> >; current->setResources(resources); collect_files(current, resources); @@ -1060,7 +1080,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) // compile resources current = assets; while(current.get()) { - KeyedVector<String8, sp<ResourceTypeSet> > *resources = + KeyedVector<String8, sp<ResourceTypeSet> > *resources = current->getResources(); ssize_t index = resources->indexOfKey(String8("values")); @@ -1069,7 +1089,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) ssize_t res; while ((res=it.next()) == NO_ERROR) { sp<AaptFile> file = it.getFile(); - res = compileResourceFile(bundle, assets, file, it.getParams(), + res = compileResourceFile(bundle, assets, file, it.getParams(), (current!=assets), &table); if (res != NO_ERROR) { hasErrors = true; @@ -1098,12 +1118,6 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) // -------------------------------------------------------------------- if (table.hasResources()) { - sp<AaptFile> resFile(getResourceFile(assets)); - if (resFile == NULL) { - fprintf(stderr, "Error: unable to generate entry for resource data\n"); - return UNKNOWN_ERROR; - } - err = table.assignResourceIds(); if (err < NO_ERROR) { return err; @@ -1211,16 +1225,24 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) } if (drawables != NULL) { - err = postProcessImages(assets, &table, drawables); - if (err != NO_ERROR) { + ResourceDirIterator it(drawables, String8("drawable")); + while ((err=it.next()) == NO_ERROR) { + err = postProcessImage(assets, &table, it.getFile()); + if (err != NO_ERROR) { + hasErrors = true; + } + } + + if (err < NO_ERROR) { hasErrors = true; } + err = NO_ERROR; } if (colors != NULL) { ResourceDirIterator it(colors, String8("color")); while ((err=it.next()) == NO_ERROR) { - err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); + err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); if (err != NO_ERROR) { hasErrors = true; } @@ -1255,7 +1277,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) if (table.validateLocalizations()) { hasErrors = true; } - + if (hasErrors) { return UNKNOWN_ERROR; } @@ -1288,7 +1310,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) ResTable finalResTable; sp<AaptFile> resFile; - + if (table.hasResources()) { sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R")); err = table.addSymbols(symbols); @@ -1296,15 +1318,35 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) return err; } - resFile = getResourceFile(assets); - if (resFile == NULL) { - fprintf(stderr, "Error: unable to generate entry for resource data\n"); - return UNKNOWN_ERROR; - } + Vector<sp<ApkSplit> >& splits = builder->getSplits(); + const size_t numSplits = splits.size(); + for (size_t i = 0; i < numSplits; i++) { + sp<ApkSplit>& split = splits.editItemAt(i); + sp<AaptFile> flattenedTable = new AaptFile(String8("resources.arsc"), + AaptGroupEntry(), String8()); + err = table.flatten(bundle, split->getResourceFilter(), flattenedTable); + if (err != NO_ERROR) { + fprintf(stderr, "Failed to generate resource table for split '%s'\n", + split->getPrintableName().string()); + return err; + } + split->addEntry(String8("resources.arsc"), flattenedTable); - err = table.flatten(bundle, resFile); - if (err < NO_ERROR) { - return err; + if (split->isBase()) { + resFile = flattenedTable; + finalResTable.add(flattenedTable->getData(), flattenedTable->getSize()); + } else { + sp<AaptFile> generatedManifest = new AaptFile(String8("AndroidManifest.xml"), + AaptGroupEntry(), String8()); + err = generateAndroidManifestForSplit(String16(assets->getPackage()), split, + generatedManifest); + if (err != NO_ERROR) { + fprintf(stderr, "Failed to generate AndroidManifest.xml for split '%s'\n", + split->getPrintableName().string()); + return err; + } + split->addEntry(String8("AndroidManifest.xml"), generatedManifest); + } } if (bundle->getPublicOutputFile()) { @@ -1321,14 +1363,10 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) fclose(fp); } - // Read resources back in, - finalResTable.add(resFile->getData(), resFile->getSize()); -#if 0 - NOISY( - printf("Generated resources:\n"); - finalResTable.print(); - ) -#endif + if (finalResTable.getTableCount() == 0 || resFile == NULL) { + fprintf(stderr, "No resource table was generated.\n"); + return UNKNOWN_ERROR; + } } // Perform a basic validation of the manifest file. This time we @@ -1425,7 +1463,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) ssize_t index = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "name"); const uint16_t* id = block.getAttributeStringValue(index, &len); if (id == NULL) { - fprintf(stderr, "%s:%d: missing name attribute in element <%s>.\n", + fprintf(stderr, "%s:%d: missing name attribute in element <%s>.\n", manifestPath.string(), block.getLineNumber(), String8(block.getElementName(&len)).string()); hasErrors = true; @@ -1583,7 +1621,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) return err; } } - + return err; } @@ -1705,7 +1743,7 @@ static status_t writeLayoutClasses( NA = idents.size(); bool deprecated = false; - + String16 comment = symbols->getComment(realClassName); fprintf(fp, "%s/** ", indentStr); if (comment.size() > 0) { @@ -1788,7 +1826,7 @@ static status_t writeLayoutClasses( if (deprecated) { fprintf(fp, "%s@Deprecated\n", indentStr); } - + fprintf(fp, "%spublic static final int[] %s = {\n" "%s", @@ -1833,9 +1871,9 @@ static status_t writeLayoutClasses( //printf("%s:%s/%s: 0x%08x\n", String8(package16).string(), // String8(attr16).string(), String8(name16).string(), typeSpecFlags); const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0; - + bool deprecated = false; - + fprintf(fp, "%s/**\n", indentStr); if (comment.size() > 0) { String8 cmt(comment); @@ -2438,7 +2476,7 @@ struct NamespaceAttributePair { status_t writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile, - const char* startTag, const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs) + const Vector<String8>& startTags, const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs) { status_t err; ResXMLTree tree; @@ -2452,15 +2490,18 @@ writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile, tree.restart(); - if (startTag != NULL) { + if (!startTags.isEmpty()) { bool haveStart = false; while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { if (code != ResXMLTree::START_TAG) { continue; } String8 tag(tree.getElementName(&len)); - if (tag == startTag) { - haveStart = true; + const size_t numStartTags = startTags.size(); + for (size_t i = 0; i < numStartTags; i++) { + if (tag == startTags[i]) { + haveStart = true; + } } break; } @@ -2547,15 +2588,17 @@ writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets) for (size_t k=0; k<K; k++) { const sp<AaptDir>& d = dirs.itemAt(k); const String8& dirName = d->getLeaf(); + Vector<String8> startTags; const char* startTag = NULL; const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs = NULL; if ((dirName == String8("layout")) || (strncmp(dirName.string(), "layout-", 7) == 0)) { tagAttrPairs = &kLayoutTagAttrPairs; } else if ((dirName == String8("xml")) || (strncmp(dirName.string(), "xml-", 4) == 0)) { - startTag = "PreferenceScreen"; + startTags.add(String8("PreferenceScreen")); + startTags.add(String8("preference-headers")); tagAttrPairs = &kXmlTagAttrPairs; } else if ((dirName == String8("menu")) || (strncmp(dirName.string(), "menu-", 5) == 0)) { - startTag = "menu"; + startTags.add(String8("menu")); tagAttrPairs = NULL; } else { continue; @@ -2568,7 +2611,7 @@ writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets) const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files = group->getFiles(); const size_t M = files.size(); for (size_t j=0; j<M; j++) { - err = writeProguardForXml(keep, files.valueAt(j), startTag, tagAttrPairs); + err = writeProguardForXml(keep, files.valueAt(j), startTags, tagAttrPairs); if (err < 0) { return err; } diff --git a/tools/aapt/ResourceFilter.cpp b/tools/aapt/ResourceFilter.cpp index 8ca852e..de8b4fc 100644 --- a/tools/aapt/ResourceFilter.cpp +++ b/tools/aapt/ResourceFilter.cpp @@ -1,119 +1,92 @@ // -// Copyright 2011 The Android Open Source Project +// Copyright 2014 The Android Open Source Project // // Build resource files from raw assets. // #include "ResourceFilter.h" +#include "AaptUtil.h" +#include "AaptConfig.h" status_t -ResourceFilter::parse(const char* arg) +WeakResourceFilter::parse(const String8& str) { - 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); - + Vector<String8> configStrs = AaptUtil::split(str, ','); + const size_t N = configStrs.size(); + mConfigs.clear(); + mConfigMask = 0; + mConfigs.resize(N); + for (size_t i = 0; i < N; i++) { + const String8& part = configStrs[i]; if (part == "en_XA") { mContainsPseudoAccented = true; } else if (part == "ar_XB") { mContainsPseudoBidi = true; } - int axis; - AxisValue value; - if (!AaptGroupEntry::parseFilterNamePart(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<AxisValue>()); - } - SortedVector<AxisValue>& sv = mData.editValueFor(axis); - sv.add(value); + std::pair<ConfigDescription, uint32_t>& entry = mConfigs.editItemAt(i); - // If it's a locale with a region, script or variant, we should also match an - // unmodified locale of the same language - if (axis == AXIS_LOCALE) { - if (value.localeValue.region[0] || value.localeValue.script[0] || - value.localeValue.variant[0]) { - AxisValue copy; - memcpy(copy.localeValue.language, value.localeValue.language, - sizeof(value.localeValue.language)); - sv.add(copy); - } + AaptLocaleValue val; + if (val.initFromFilterString(part)) { + // For backwards compatibility, we accept configurations that + // only specify locale in the standard 'en_US' format. + val.writeTo(&entry.first); + } else if (!AaptConfig::parse(part, &entry.first)) { + fprintf(stderr, "Invalid configuration: %s\n", part.string()); + return UNKNOWN_ERROR; } - p = q; - if (!*p) break; - p++; + + entry.second = mDefault.diff(entry.first); + + // Ignore the version + entry.second &= ~ResTable_config::CONFIG_VERSION; + + mConfigMask |= entry.second; } return NO_ERROR; } bool -ResourceFilter::isEmpty() const -{ - return mData.size() == 0; -} - -bool -ResourceFilter::match(int axis, const AxisValue& value) const +WeakResourceFilter::match(const ResTable_config& config) const { - if (value.intValue == 0 && (value.localeValue.language[0] == 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 + uint32_t mask = mDefault.diff(config); + if ((mConfigMask & mask) == 0) { + // The two configurations don't have any common axis. return true; } - const SortedVector<AxisValue>& 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; + const size_t N = mConfigs.size(); + for (size_t i = 0; i < N; i++) { + const std::pair<ConfigDescription, uint32_t>& entry = mConfigs[i]; + uint32_t diff = entry.first.diff(config); + if ((diff & entry.second) == 0) { + return true; + } else if ((diff & entry.second) == ResTable_config::CONFIG_LOCALE) { + // If the locales differ, but the languages are the same and + // the locale we are matching only has a language specified, + // we match. + if (config.language[0] && memcmp(config.language, entry.first.language, sizeof(config.language)) == 0) { + if (config.country[0] == 0) { + return true; + } + } } } - return true; + return false; } -const SortedVector<AxisValue>* ResourceFilter::configsForAxis(int axis) const -{ - ssize_t index = mData.indexOfKey(axis); - if (index < 0) { - return NULL; +status_t +StrongResourceFilter::parse(const String8& str) { + Vector<String8> configStrs = AaptUtil::split(str, ','); + ConfigDescription config; + mConfigs.clear(); + for (size_t i = 0; i < configStrs.size(); i++) { + if (!AaptConfig::parse(configStrs[i], &config)) { + fprintf(stderr, "Invalid configuration: %s\n", configStrs[i].string()); + return UNKNOWN_ERROR; + } + mConfigs.insert(config); } - return &mData.valueAt(index); + return NO_ERROR; } diff --git a/tools/aapt/ResourceFilter.h b/tools/aapt/ResourceFilter.h index c57770e..f459584 100644 --- a/tools/aapt/ResourceFilter.h +++ b/tools/aapt/ResourceFilter.h @@ -7,31 +7,137 @@ #ifndef RESOURCE_FILTER_H #define RESOURCE_FILTER_H +#include <androidfw/ResourceTypes.h> +#include <set> +#include <utility> +#include <utils/Errors.h> +#include <utils/String8.h> +#include <utils/StrongPointer.h> +#include <utils/Vector.h> + #include "AaptAssets.h" +#include "ConfigDescription.h" + +class ResourceFilter : public virtual android::RefBase { +public: + virtual bool match(const android::ResTable_config& config) const = 0; +}; /** * Implements logic for parsing and handling "-c" and "--preferred-configurations" * options. */ -class ResourceFilter -{ +class WeakResourceFilter : public ResourceFilter { public: - ResourceFilter() : mData(), mContainsPseudoAccented(false), - mContainsPseudoBidi(false) {} - status_t parse(const char* arg); - bool isEmpty() const; - bool match(int axis, const ResTable_config& config) const; - bool match(const ResTable_config& config) const; - const SortedVector<AxisValue>* configsForAxis(int axis) const; - inline bool containsPseudo() const { return mContainsPseudoAccented; } - inline bool containsPseudoBidi() const { return mContainsPseudoBidi; } + WeakResourceFilter() + : mContainsPseudoAccented(false) + , mContainsPseudoBidi(false) {} + + android::status_t parse(const android::String8& str); + + bool match(const android::ResTable_config& config) const; + + inline bool isEmpty() const { + return mConfigMask == 0; + } + + inline bool containsPseudo() const { + return mContainsPseudoAccented; + } + + inline bool containsPseudoBidi() const { + return mContainsPseudoBidi; + } private: - bool match(int axis, const AxisValue& value) const; + ConfigDescription mDefault; + uint32_t mConfigMask; + android::Vector<std::pair<ConfigDescription, uint32_t> > mConfigs; - KeyedVector<int,SortedVector<AxisValue> > mData; bool mContainsPseudoAccented; bool mContainsPseudoBidi; }; +/** + * Matches resources that have at least one of the configurations + * that this filter is looking for. In order to match a configuration, + * the resource must have the exact same configuration. + * + * This filter acts as a logical OR when matching resources. + * + * For example, if the filter is looking for resources with + * fr-land, de-land, or sw600dp: + * + * (PASS) fr-land + * (FAIL) fr + * (PASS) de-land + * (FAIL) de + * (FAIL) de-sw600dp + * (PASS) sw600dp + * (FAIL) sw600dp-land + */ +class StrongResourceFilter : public ResourceFilter { +public: + StrongResourceFilter() {} + StrongResourceFilter(const std::set<ConfigDescription>& configs) + : mConfigs(configs) {} + + android::status_t parse(const android::String8& str); + + bool match(const android::ResTable_config& config) const { + std::set<ConfigDescription>::const_iterator iter = mConfigs.begin(); + for (; iter != mConfigs.end(); iter++) { + if (iter->compare(config) == 0) { + return true; + } + } + return false; + } + + inline const std::set<ConfigDescription>& getConfigs() const { + return mConfigs; + } + +private: + std::set<ConfigDescription> mConfigs; +}; + +/** + * Negates the response of the target filter. + */ +class InverseResourceFilter : public ResourceFilter { +public: + InverseResourceFilter(const android::sp<ResourceFilter>& filter) + : mFilter(filter) {} + + bool match(const android::ResTable_config& config) const { + return !mFilter->match(config); + } + +private: + const android::sp<ResourceFilter> mFilter; +}; + +/** + * A logical AND of all the added filters. + */ +class AndResourceFilter : public ResourceFilter { +public: + void addFilter(const android::sp<ResourceFilter>& filter) { + mFilters.add(filter); + } + + bool match(const android::ResTable_config& config) const { + const size_t N = mFilters.size(); + for (size_t i = 0; i < N; i++) { + if (!mFilters[i]->match(config)) { + return false; + } + } + return true; + } + +private: + android::Vector<android::sp<ResourceFilter> > mFilters; +}; #endif diff --git a/tools/aapt/ResourceIdCache.cpp b/tools/aapt/ResourceIdCache.cpp index e03f4f6..d60a07f 100644 --- a/tools/aapt/ResourceIdCache.cpp +++ b/tools/aapt/ResourceIdCache.cpp @@ -98,10 +98,10 @@ uint32_t ResourceIdCache::store(const android::String16& package, void ResourceIdCache::dump() { printf("ResourceIdCache dump:\n"); - printf("Size: %ld\n", mIdMap.size()); - printf("Hits: %ld\n", mHits); - printf("Misses: %ld\n", mMisses); - printf("(Collisions: %ld)\n", mCollisions); + printf("Size: %zd\n", mIdMap.size()); + printf("Hits: %zd\n", mHits); + printf("Misses: %zd\n", mMisses); + printf("(Collisions: %zd)\n", mCollisions); } } diff --git a/tools/aapt/ResourceIdCache.h b/tools/aapt/ResourceIdCache.h index e6bcda2..3acdee1 100644 --- a/tools/aapt/ResourceIdCache.h +++ b/tools/aapt/ResourceIdCache.h @@ -6,18 +6,20 @@ #ifndef RESOURCE_ID_CACHE_H #define RESOURCE_ID_CACHE_H +#include <utils/String16.h> + namespace android { class ResourceIdCache { public: - static uint32_t lookup(const android::String16& package, - const android::String16& type, - const android::String16& name, + static uint32_t lookup(const String16& package, + const String16& type, + const String16& name, bool onlyPublic); - static uint32_t store(const android::String16& package, - const android::String16& type, - const android::String16& name, + static uint32_t store(const String16& package, + const String16& type, + const String16& name, bool onlyPublic, uint32_t resId); diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index cf271a9..efbba40 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -1715,7 +1715,7 @@ status_t compileResourceFile(Bundle* bundle, ResourceTable::ResourceTable(Bundle* bundle, const String16& assetsPackage) : mAssetsPackage(assetsPackage), mNextPackageId(1), mHaveAppPackage(false), - mIsAppPackage(!bundle->getExtending()), + mIsAppPackage(!bundle->getExtending()), mIsSharedLibrary(bundle->getBuildSharedLibrary()), mNumLocal(0), mBundle(bundle) { @@ -1737,8 +1737,9 @@ status_t ResourceTable::addIncludedResources(Bundle* bundle, const sp<AaptAssets const size_t N = incl.getBasePackageCount(); for (size_t phase=0; phase<2; phase++) { for (size_t i=0; i<N; i++) { - String16 name(incl.getBasePackageName(i)); + const String16 name = incl.getBasePackageName(i); uint32_t id = incl.getBasePackageId(i); + // First time through: only add base packages (id // is not 0); second time through add the other // packages. @@ -1764,7 +1765,7 @@ status_t ResourceTable::addIncludedResources(Bundle* bundle, const sp<AaptAssets } } if (id != 0) { - NOISY(printf("Including package %s with ID=%d\n", + NOISY(fprintf(stderr, "Including package %s with ID=%d\n", String8(name).string(), id)); sp<Package> p = new Package(name, id); mPackages.add(name, p); @@ -2094,10 +2095,10 @@ bool ResourceTable::hasResources() const { return mNumLocal > 0; } -sp<AaptFile> ResourceTable::flatten(Bundle* bundle) +sp<AaptFile> ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& filter) { sp<AaptFile> data = new AaptFile(String8(), AaptGroupEntry(), String8()); - status_t err = flatten(bundle, data); + status_t err = flatten(bundle, filter, data); return err == NO_ERROR ? data : NULL; } @@ -2657,8 +2658,8 @@ ResourceTable::validateLocalizations(void) } // Check that all requested localizations are present for this string - if (mBundle->getConfigurations() != NULL && mBundle->getRequireLocalization()) { - const char* allConfigs = mBundle->getConfigurations(); + if (mBundle->getConfigurations().size() > 0 && mBundle->getRequireLocalization()) { + const char* allConfigs = mBundle->getConfigurations().string(); const char* start = allConfigs; const char* comma; @@ -2712,14 +2713,8 @@ ResourceTable::validateLocalizations(void) return err; } -status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) +status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& filter, const sp<AaptFile>& dest) { - ResourceFilter filter; - status_t err = filter.parse(bundle->getConfigurations()); - if (err != NO_ERROR) { - return err; - } - const ConfigDescription nullConfig; const size_t N = mOrderedPackages.size(); @@ -2729,6 +2724,9 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) bool useUTF8 = !bundle->getUTF16StringsOption(); + // The libraries this table references. + Vector<sp<Package> > libraryPackages; + // Iterate through all data, collecting all values (strings, // references, etc). StringPool valueStrings(useUTF8); @@ -2736,8 +2734,22 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) for (pi=0; pi<N; pi++) { sp<Package> p = mOrderedPackages.itemAt(pi); if (p->getTypes().size() == 0) { - // Empty, skip! + // Empty, this is an imported package being used as + // a shared library. We do not flatten this package. + if (p->getAssignedId() != 0x01 && p->getName() != String16("android")) { + // This is not the base Android package, and it is a library + // so we must add a reference to the library when flattening. + libraryPackages.add(p); + } continue; + } else if (p->getAssignedId() == 0x00) { + if (!bundle->getBuildSharedLibrary()) { + fprintf(stderr, "ERROR: Package %s can not have ID=0x00 unless building a shared library.", + String8(p->getName()).string()); + return UNKNOWN_ERROR; + } + // If this is a shared library, we also include ourselves as an entry. + libraryPackages.add(p); } StringPool typeStrings(useUTF8); @@ -2777,7 +2789,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 (filterable && !filter.match(config)) { + if (filterable && !filter->match(config)) { continue; } sp<Entry> e = c->getEntries().valueAt(ei); @@ -2820,7 +2832,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) } ssize_t strAmt = 0; - + // Now build the array of package chunks. Vector<sp<AaptFile> > flatPackages; for (pi=0; pi<N; pi++) { @@ -2869,6 +2881,12 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) return amt; } + status_t err = flattenLibraryTable(data, libraryPackages); + if (err != NO_ERROR) { + fprintf(stderr, "ERROR: failed to write library table\n"); + return err; + } + // Build the type chunks inside of this package. for (size_t ti=0; ti<N; ti++) { // Retrieve them in the same order as the type string block. @@ -2919,11 +2937,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 (filterable && !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 (filterable && !filter.match(cl->getEntries().keyAt(cj))) { + if (filterable && !filter->match(cl->getEntries().keyAt(cj))) { continue; } typeSpecFlags[ei] |= htodl( @@ -2965,7 +2983,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) config.screenHeightDp, config.layoutDirection)); - if (filterable && !filter.match(config)) { + if (filterable && !filter->match(config)) { continue; } @@ -3038,13 +3056,21 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) tHeader->header.size = htodl(data->getSize()-typeStart); } + bool missing_entry = false; + const char* log_prefix = bundle->getErrorOnMissingConfigEntry() ? + "error" : "warning"; for (size_t i = 0; i < N; ++i) { if (!validResources[i]) { sp<ConfigList> c = t->getOrderedConfigs().itemAt(i); - fprintf(stderr, "warning: no entries written for %s/%s\n", + fprintf(stderr, "%s: no entries written for %s/%s\n", log_prefix, String8(typeName).string(), String8(c->getName()).string()); + missing_entry = true; } } + if (bundle->getErrorOnMissingConfigEntry() && missing_entry) { + fprintf(stderr, "Error: Missing entries, quit!\n"); + return NOT_ENOUGH_DATA; + } } // Fill in the rest of the package information. @@ -3076,7 +3102,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) } ssize_t strStart = dest->getSize(); - err = valueStrings.writeStringBlock(dest); + status_t err = valueStrings.writeStringBlock(dest); if (err != NO_ERROR) { return err; } @@ -3087,7 +3113,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) fprintf(stderr, "**** value strings: %d\n", amt); fprintf(stderr, "**** total strings: %d\n", strAmt); #endif - + for (pi=0; pi<flatPackages.size(); pi++) { err = dest->writeData(flatPackages[pi]->getData(), flatPackages[pi]->getSize()); @@ -3112,6 +3138,38 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) return NO_ERROR; } +status_t ResourceTable::flattenLibraryTable(const sp<AaptFile>& dest, const Vector<sp<Package> >& libs) { + // Write out the library table if necessary + if (libs.size() > 0) { + NOISY(fprintf(stderr, "Writing library reference table\n")); + + const size_t libStart = dest->getSize(); + const size_t count = libs.size(); + ResTable_lib_header* libHeader = (ResTable_lib_header*) dest->editDataInRange(libStart, sizeof(ResTable_lib_header)); + + memset(libHeader, 0, sizeof(*libHeader)); + libHeader->header.type = htods(RES_TABLE_LIBRARY_TYPE); + libHeader->header.headerSize = htods(sizeof(*libHeader)); + libHeader->header.size = htodl(sizeof(*libHeader) + (sizeof(ResTable_lib_entry) * count)); + libHeader->count = htodl(count); + + // Write the library entries + for (size_t i = 0; i < count; i++) { + const size_t entryStart = dest->getSize(); + sp<Package> libPackage = libs[i]; + NOISY(fprintf(stderr, " Entry %s -> 0x%02x\n", + String8(libPackage->getName()).string(), + (uint8_t)libPackage->getAssignedId())); + + ResTable_lib_entry* entry = (ResTable_lib_entry*) dest->editDataInRange(entryStart, sizeof(ResTable_lib_entry)); + memset(entry, 0, sizeof(*entry)); + entry->packageId = htodl(libPackage->getAssignedId()); + strcpy16_htod(entry->packageName, libPackage->getName().string()); + } + } + return NO_ERROR; +} + void ResourceTable::writePublicDefinitions(const String16& package, FILE* fp) { fprintf(fp, @@ -3881,7 +3939,7 @@ sp<ResourceTable::Package> ResourceTable::getPackage(const String16& package) return NULL; } mHaveAppPackage = true; - p = new Package(package, 127); + p = new Package(package, mIsSharedLibrary ? 0 : 127); } else { p = new Package(package, mNextPackageId); } diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h index 75005cd..a73993c 100644 --- a/tools/aapt/ResourceTable.h +++ b/tools/aapt/ResourceTable.h @@ -7,8 +7,10 @@ #ifndef RESOURCE_TABLE_H #define RESOURCE_TABLE_H +#include "ConfigDescription.h" #include "StringPool.h" #include "SourcePos.h" +#include "ResourceFilter.h" #include <set> #include <map> @@ -76,37 +78,6 @@ public: class Type; class Entry; - struct ConfigDescription : public ResTable_config { - ConfigDescription() { - memset(this, 0, sizeof(*this)); - size = sizeof(ResTable_config); - } - ConfigDescription(const ResTable_config&o) { - *static_cast<ResTable_config*>(this) = o; - size = sizeof(ResTable_config); - } - ConfigDescription(const ConfigDescription&o) { - *static_cast<ResTable_config*>(this) = o; - } - - ConfigDescription& operator=(const ResTable_config& o) { - *static_cast<ResTable_config*>(this) = o; - size = sizeof(ResTable_config); - return *this; - } - ConfigDescription& operator=(const ConfigDescription& o) { - *static_cast<ResTable_config*>(this) = o; - return *this; - } - - inline bool operator<(const ConfigDescription& o) const { return compare(o) < 0; } - inline bool operator<=(const ConfigDescription& o) const { return compare(o) <= 0; } - inline bool operator==(const ConfigDescription& o) const { return compare(o) == 0; } - inline bool operator!=(const ConfigDescription& o) const { return compare(o) != 0; } - inline bool operator>=(const ConfigDescription& o) const { return compare(o) >= 0; } - inline bool operator>(const ConfigDescription& o) const { return compare(o) > 0; } - }; - ResourceTable(Bundle* bundle, const String16& assetsPackage); status_t addIncludedResources(Bundle* bundle, const sp<AaptAssets>& assets); @@ -182,7 +153,7 @@ public: size_t numLocalResources() const; bool hasResources() const; - sp<AaptFile> flatten(Bundle*); + sp<AaptFile> flatten(Bundle* bundle, const sp<const ResourceFilter>& filter); static inline uint32_t makeResId(uint32_t packageId, uint32_t typeId, @@ -223,7 +194,8 @@ public: void addLocalization(const String16& name, const String8& locale, const SourcePos& src); status_t validateLocalizations(void); - status_t flatten(Bundle*, const sp<AaptFile>& dest); + status_t flatten(Bundle* bundle, const sp<const ResourceFilter>& filter, const sp<AaptFile>& dest); + status_t flattenLibraryTable(const sp<AaptFile>& dest, const Vector<sp<Package> >& libs); void writePublicDefinitions(const String16& package, FILE* fp); @@ -546,6 +518,7 @@ private: uint32_t mNextPackageId; bool mHaveAppPackage; bool mIsAppPackage; + bool mIsSharedLibrary; size_t mNumLocal; SourcePos mCurrentXmlPos; Bundle* mBundle; diff --git a/tools/aapt/printapk.cpp b/tools/aapt/printapk.cpp index 4cf73d8..def6e2e 100644 --- a/tools/aapt/printapk.cpp +++ b/tools/aapt/printapk.cpp @@ -115,8 +115,8 @@ main(int argc, char** argv) size_t basePackageCount = res.getBasePackageCount(); printf("Base Packages: %d\n", (int)basePackageCount); for (size_t bpIndex=0; bpIndex<basePackageCount; bpIndex++) { - const char16_t* ch = res.getBasePackageName(bpIndex); - String8 s = String8(String16(ch)); + const String16 ch = res.getBasePackageName(bpIndex); + String8 s = String8(ch); printf(" [%3d] %s\n", (int)bpIndex, s.string()); } #endif diff --git a/tools/aapt/tests/AaptConfig_test.cpp b/tools/aapt/tests/AaptConfig_test.cpp new file mode 100644 index 0000000..e795d81 --- /dev/null +++ b/tools/aapt/tests/AaptConfig_test.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2014 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +#include <utils/String8.h> +#include <gtest/gtest.h> + +#include "AaptConfig.h" +#include "ConfigDescription.h" +#include "TestHelper.h" + +using android::String8; + +static ::testing::AssertionResult TestParse(const String8& input, ConfigDescription* config=NULL) { + if (AaptConfig::parse(String8(input), config)) { + return ::testing::AssertionSuccess() << input << " was successfully parsed"; + } + return ::testing::AssertionFailure() << input << " could not be parsed"; +} + +static ::testing::AssertionResult TestParse(const char* input, ConfigDescription* config=NULL) { + return TestParse(String8(input), config); +} + +TEST(AaptConfigTest, ParseFailWhenQualifiersAreOutOfOrder) { + EXPECT_FALSE(TestParse("en-sw600dp-ldrtl")); + EXPECT_FALSE(TestParse("land-en")); + EXPECT_FALSE(TestParse("hdpi-320dpi")); +} + +TEST(AaptConfigTest, ParseFailWhenQualifiersAreNotMatched) { + EXPECT_FALSE(TestParse("en-sw600dp-ILLEGAL")); +} + +TEST(AaptConfigTest, ParseFailWhenQualifiersHaveTrailingDash) { + EXPECT_FALSE(TestParse("en-sw600dp-land-")); +} + +TEST(AaptConfigTest, ParseBasicQualifiers) { + ConfigDescription config; + EXPECT_TRUE(TestParse("", &config)); + EXPECT_EQ(String8(""), config.toString()); + + EXPECT_TRUE(TestParse("fr-land", &config)); + EXPECT_EQ(String8("fr-land"), config.toString()); + + EXPECT_TRUE(TestParse("mcc310-pl-sw720dp-normal-long-port-night-" + "xhdpi-keyssoft-qwerty-navexposed-nonav", &config)); + EXPECT_EQ(String8("mcc310-pl-sw720dp-normal-long-port-night-" + "xhdpi-keyssoft-qwerty-navexposed-nonav-v13"), config.toString()); +} + +TEST(AaptConfigTest, ParseLocales) { + ConfigDescription config; + EXPECT_TRUE(TestParse("en-rUS", &config)); + EXPECT_EQ(String8("en-US"), config.toString()); +} + +TEST(AaptConfigTest, ParseQualifierAddedInApi13) { + ConfigDescription config; + EXPECT_TRUE(TestParse("sw600dp", &config)); + EXPECT_EQ(String8("sw600dp-v13"), config.toString()); + + EXPECT_TRUE(TestParse("sw600dp-v8", &config)); + EXPECT_EQ(String8("sw600dp-v13"), config.toString()); +} diff --git a/tools/aapt/tests/AaptGroupEntry_test.cpp b/tools/aapt/tests/AaptGroupEntry_test.cpp new file mode 100644 index 0000000..7348a08 --- /dev/null +++ b/tools/aapt/tests/AaptGroupEntry_test.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2014 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +#include <utils/String8.h> +#include <gtest/gtest.h> + +#include "AaptAssets.h" +#include "ResourceFilter.h" +#include "TestHelper.h" + +using android::String8; + +static ::testing::AssertionResult TestParse(AaptGroupEntry& entry, const String8& dirName, + String8* outType) { + if (entry.initFromDirName(dirName, outType)) { + return ::testing::AssertionSuccess() << dirName << " was successfully parsed"; + } + return ::testing::AssertionFailure() << dirName << " could not be parsed"; +} + +static ::testing::AssertionResult TestParse(AaptGroupEntry& entry, const char* input, + String8* outType) { + return TestParse(entry, String8(input), outType); +} + +TEST(AaptGroupEntryTest, ParseNoQualifier) { + AaptGroupEntry entry; + String8 type; + EXPECT_TRUE(TestParse(entry, "menu", &type)); + EXPECT_EQ(String8("menu"), type); +} + +TEST(AaptGroupEntryTest, ParseCorrectType) { + AaptGroupEntry entry; + String8 type; + EXPECT_TRUE(TestParse(entry, "anim", &type)); + EXPECT_EQ(String8("anim"), type); + + EXPECT_TRUE(TestParse(entry, "animator", &type)); + EXPECT_EQ(String8("animator"), type); +} diff --git a/tools/aapt/tests/ResourceFilter_test.cpp b/tools/aapt/tests/ResourceFilter_test.cpp new file mode 100644 index 0000000..30697bb --- /dev/null +++ b/tools/aapt/tests/ResourceFilter_test.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2014 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +#include <androidfw/ResourceTypes.h> +#include <utils/String8.h> +#include <gtest/gtest.h> + +#include "AaptConfig.h" +#include "ResourceFilter.h" +#include "ConfigDescription.h" + +using android::String8; + +// In this context, 'Axis' represents a particular field in the configuration, +// such as language or density. + +TEST(WeakResourceFilterTest, EmptyFilterMatchesAnything) { + WeakResourceFilter filter; + ASSERT_EQ(NO_ERROR, filter.parse(String8(""))); + + ConfigDescription config; + config.density = 320; + + EXPECT_TRUE(filter.match(config)); + + config.language[0] = 'f'; + config.language[1] = 'r'; + + EXPECT_TRUE(filter.match(config)); +} + +TEST(WeakResourceFilterTest, MatchesConfigWithUnrelatedAxis) { + WeakResourceFilter filter; + ASSERT_EQ(NO_ERROR, filter.parse(String8("fr"))); + + ConfigDescription config; + config.density = 320; + + EXPECT_TRUE(filter.match(config)); +} + +TEST(WeakResourceFilterTest, MatchesConfigWithSameValueAxis) { + WeakResourceFilter filter; + ASSERT_EQ(NO_ERROR, filter.parse(String8("fr"))); + + ConfigDescription config; + config.language[0] = 'f'; + config.language[1] = 'r'; + + EXPECT_TRUE(filter.match(config)); +} + +TEST(WeakResourceFilterTest, MatchesConfigWithSameValueAxisAndOtherUnrelatedAxis) { + WeakResourceFilter filter; + ASSERT_EQ(NO_ERROR, filter.parse(String8("fr"))); + + ConfigDescription config; + config.language[0] = 'f'; + config.language[1] = 'r'; + config.density = 320; + + EXPECT_TRUE(filter.match(config)); +} + +TEST(WeakResourceFilterTest, DoesNotMatchConfigWithDifferentValueAxis) { + WeakResourceFilter filter; + ASSERT_EQ(NO_ERROR, filter.parse(String8("fr"))); + + ConfigDescription config; + config.language[0] = 'd'; + config.language[1] = 'e'; + + EXPECT_FALSE(filter.match(config)); +} + +TEST(WeakResourceFilterTest, MatchesConfigWithSameLanguageButNoRegionSpecified) { + WeakResourceFilter filter; + ASSERT_EQ(NO_ERROR, filter.parse(String8("de-rDE"))); + + ConfigDescription config; + config.language[0] = 'd'; + config.language[1] = 'e'; + + EXPECT_TRUE(filter.match(config)); +} + +TEST(WeakResourceFilterTest, ParsesStandardLocaleOnlyString) { + WeakResourceFilter filter; + EXPECT_EQ(NO_ERROR, filter.parse(String8("de_DE"))); +} + +TEST(WeakResourceFilterTest, IgnoresVersion) { + WeakResourceFilter filter; + ASSERT_EQ(NO_ERROR, filter.parse(String8("normal-v4"))); + + ConfigDescription config; + config.smallestScreenWidthDp = 600; + config.version = 13; + + // The configs don't match on any axis besides version, which should be ignored. + EXPECT_TRUE(filter.match(config)); +} + +TEST(WeakResourceFilterTest, MatchesConfigWithRegion) { + WeakResourceFilter filter; + ASSERT_EQ(NO_ERROR, filter.parse(String8("kok,kok_IN,kok_419"))); + + ConfigDescription config; + AaptLocaleValue val; + ASSERT_TRUE(val.initFromFilterString(String8("kok_IN"))); + val.writeTo(&config); + + EXPECT_TRUE(filter.match(config)); +} + diff --git a/tools/aapt/tests/TestHelper.h b/tools/aapt/tests/TestHelper.h new file mode 100644 index 0000000..7917483 --- /dev/null +++ b/tools/aapt/tests/TestHelper.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2014 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +#ifndef __TEST_HELPER_H +#define __TEST_HELPER_H + +#include <utils/String8.h> + +namespace android { + +/** + * Stream operator for nicely printing String8's in gtest output. + */ +inline std::ostream& operator<<(std::ostream& stream, const String8& str) { + return stream << str.string(); +} + +} + +#endif diff --git a/tools/aidl/aidl.cpp b/tools/aidl/aidl.cpp index a84d743..9c1867e 100644 --- a/tools/aidl/aidl.cpp +++ b/tools/aidl/aidl.cpp @@ -207,7 +207,7 @@ check_filename(const char* filename, const char* package, buffer_type* name) p = strchr(name->data, '.'); len = p ? p-name->data : strlen(name->data); expected.append(name->data, len); - + expected += ".aidl"; len = fn.length(); @@ -473,7 +473,7 @@ check_method(const char* filename, int kind, method_type* m) err = 1; goto next; } - + if (!(kind == INTERFACE_TYPE_BINDER ? t->CanWriteToParcel() : t->CanWriteToRpcData())) { fprintf(stderr, "%s:%d parameter %d: '%s %s' can't be marshalled.\n", filename, m->type.type.lineno, index, @@ -536,7 +536,7 @@ check_method(const char* filename, int kind, method_type* m) filename, m->name.lineno, index, arg->name.data); err = 1; } - + next: index++; arg = arg->next; @@ -797,7 +797,7 @@ parse_preprocessed_file(const string& filename) //printf("%s:%d:...%s...%s...%s...\n", filename.c_str(), lineno, // type, packagename, classname); document_item_type* doc; - + if (0 == strcmp("parcelable", type)) { user_data_type* parcl = (user_data_type*)malloc( sizeof(user_data_type)); @@ -1104,13 +1104,13 @@ preprocess_aidl(const Options& options) } // write preprocessed file - int fd = open( options.outputFileName.c_str(), + int fd = open( options.outputFileName.c_str(), O_RDWR|O_CREAT|O_TRUNC|O_BINARY, #ifdef HAVE_MS_C_RUNTIME _S_IREAD|_S_IWRITE); -#else +#else S_IRUSR|S_IWUSR|S_IRGRP); -#endif +#endif if (fd == -1) { fprintf(stderr, "aidl: could not open file for write: %s\n", options.outputFileName.c_str()); diff --git a/tools/layoutlib/bridge/.classpath b/tools/layoutlib/bridge/.classpath index 2e4274d..aef3efa 100644 --- a/tools/layoutlib/bridge/.classpath +++ b/tools/layoutlib/bridge/.classpath @@ -1,12 +1,14 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> <classpathentry excluding="org/kxml2/io/" kind="src" path="src"/> + <classpathentry kind="src" path="tests/src"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> - <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/layoutlib_api/layoutlib_api-prebuilt.jar"/> + <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/layoutlib_api/layoutlib_api-prebuilt.jar" sourcepath="/ANDROID_SRC/tools/base/layoutlib-api/src/main"/> <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/kxml2/kxml2-2.3.0.jar" sourcepath="/ANDROID_PLAT_SRC/dalvik/libcore/xml/src/main/java"/> <classpathentry kind="var" path="ANDROID_PLAT_SRC/out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/javalib.jar" sourcepath="/ANDROID_PLAT_SRC/frameworks/base"/> <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/ninepatch/ninepatch-prebuilt.jar"/> <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/tools-common/tools-common-prebuilt.jar"/> <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/icu4j/icu4j.jar"/> + <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java b/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java index 224eac6..4603b63 100644 --- a/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java +++ b/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java @@ -48,6 +48,20 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate; } @LayoutlibDelegate + /*package*/ static long nGetMultipleIntMethod(Class<?> targetClass, String methodName, + int numParams) { + // TODO: return the right thing. + return 0; + } + + @LayoutlibDelegate + /*package*/ static long nGetMultipleFloatMethod(Class<?> targetClass, String methodName, + int numParams) { + // TODO: return the right thing. + return 0; + } + + @LayoutlibDelegate /*package*/ static void nCallIntMethod(Object target, long methodID, int arg) { // do nothing } @@ -56,4 +70,40 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate; /*package*/ static void nCallFloatMethod(Object target, long methodID, float arg) { // do nothing } + + @LayoutlibDelegate + /*package*/ static void nCallTwoIntMethod(Object target, long methodID, int arg1, + int arg2) { + // do nothing + } + + @LayoutlibDelegate + /*package*/ static void nCallFourIntMethod(Object target, long methodID, int arg1, + int arg2, int arg3, int arg4) { + // do nothing + } + + @LayoutlibDelegate + /*package*/ static void nCallMultipleIntMethod(Object target, long methodID, + int[] args) { + // do nothing + } + + @LayoutlibDelegate + /*package*/ static void nCallTwoFloatMethod(Object target, long methodID, float arg1, + float arg2) { + // do nothing + } + + @LayoutlibDelegate + /*package*/ static void nCallFourFloatMethod(Object target, long methodID, float arg1, + float arg2, float arg3, float arg4) { + // do nothing + } + + @LayoutlibDelegate + /*package*/ static void nCallMultipleFloatMethod(Object target, long methodID, + float[] args) { + // do nothing + } } diff --git a/tools/layoutlib/bridge/src/android/content/res/AssetManager_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/AssetManager_Delegate.java index 5c2b793..914a359 100644 --- a/tools/layoutlib/bridge/src/android/content/res/AssetManager_Delegate.java +++ b/tools/layoutlib/bridge/src/android/content/res/AssetManager_Delegate.java @@ -40,9 +40,6 @@ public class AssetManager_Delegate { @LayoutlibDelegate /*package*/ static void applyThemeStyle(long theme, int styleRes, boolean force) { - Resources_Theme_Delegate delegate = Resources_Theme_Delegate.getDelegateManager() - .getDelegate(theme); - delegate.mThemeResId = styleRes; - delegate.force = force; + Resources_Theme_Delegate.getDelegateManager().getDelegate(theme).force = force; } } diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java index 8794452..dd573be 100644 --- a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java +++ b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java @@ -51,6 +51,7 @@ public final class BridgeResources extends Resources { private BridgeContext mContext; private IProjectCallback mProjectCallback; private boolean[] mPlatformResourceFlag = new boolean[1]; + private TypedValue mTmpValue = new TypedValue(); /** * Simpler wrapper around FileInputStream. This is used when the input stream represent @@ -154,6 +155,11 @@ public final class BridgeResources extends Resources { @Override public Drawable getDrawable(int id) throws NotFoundException { + return getDrawable(id, null); + } + + @Override + public Drawable getDrawable(int id, Theme theme) { Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag); if (value != null) { diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java index 446d139..cc621c4 100644 --- a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java +++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java @@ -52,13 +52,13 @@ public final class BridgeTypedArray extends TypedArray { private final BridgeContext mContext; private final boolean mPlatformFile; - private ResourceValue[] mResourceData; - private String[] mNames; - private boolean[] mIsFramework; + private final ResourceValue[] mResourceData; + private final String[] mNames; + private final boolean[] mIsFramework; public BridgeTypedArray(BridgeResources resources, BridgeContext context, int len, boolean platformFile) { - super(null, null, null, 0); + super(resources, null, null, 0); mBridgeResources = resources; mContext = context; mPlatformFile = platformFile; @@ -81,8 +81,8 @@ public final class BridgeTypedArray extends TypedArray { } /** - * Seals the array after all calls to {@link #bridgeSetValue(int, String, ResourceValue)} have - * been done. + * Seals the array after all calls to + * {@link #bridgeSetValue(int, String, boolean, ResourceValue)} have been done. * <p/>This allows to compute the list of non default values, permitting * {@link #getIndexCount()} to return the proper value. */ @@ -252,7 +252,7 @@ public final class BridgeTypedArray extends TypedArray { for (String keyword : keywords) { Integer i = map.get(keyword.trim()); if (i != null) { - result |= i.intValue(); + result |= i; } else { Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT, String.format( @@ -731,7 +731,7 @@ public final class BridgeTypedArray extends TypedArray { } // not a direct id valid reference? resolve it - Integer idValue = null; + Integer idValue; if (resValue.isFramework()) { idValue = Bridge.getResourceId(resValue.getResourceType(), @@ -742,7 +742,7 @@ public final class BridgeTypedArray extends TypedArray { } if (idValue != null) { - return idValue.intValue(); + return idValue; } Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_RESOLVE, @@ -753,6 +753,12 @@ public final class BridgeTypedArray extends TypedArray { return defValue; } + @Override + public int getThemeAttributeId(int index, int defValue) { + // TODO: Get the right Theme Attribute ID to enable caching of the drawables. + return defValue; + } + /** * Retrieve the Drawable for the attribute at <var>index</var>. This * gets the resource ID of the selected attribute, and uses @@ -854,6 +860,7 @@ public final class BridgeTypedArray extends TypedArray { */ @Override public boolean hasValue(int index) { + //noinspection SimplifiableIfStatement if (index < 0 || index >= mResourceData.length) { return false; } diff --git a/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java new file mode 100644 index 0000000..112250d --- /dev/null +++ b/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2014 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +package android.content.res; + +import java.util.Locale; + +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; +import com.ibm.icu.util.ULocale; + +/** + * Delegate used to provide new implementation of a select few methods of {@link Resources} + * + * Through the layoutlib_create tool, the original methods of Resources have been replaced + * by calls to methods of the same name in this delegate class. + * + */ +public class Resources_Delegate { + + @LayoutlibDelegate + /*package*/ static String localeToLanguageTag(Resources res, Locale locale) { + return ULocale.forLocale(locale).toLanguageTag(); + } +} diff --git a/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java index b89d15f..31d1594 100644 --- a/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java +++ b/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java @@ -39,8 +39,6 @@ import android.util.TypedValue; */ public class Resources_Theme_Delegate { - // Resource identifier for the theme. - int mThemeResId; // Whether to use the Theme.mThemeResId as primary theme. boolean force; @@ -103,7 +101,7 @@ public class Resources_Theme_Delegate { private static boolean setupResources(Theme thisTheme) { Resources_Theme_Delegate themeDelegate = sManager.getDelegate(thisTheme.getNativeTheme()); - StyleResourceValue style = resolveStyle(themeDelegate.mThemeResId); + StyleResourceValue style = resolveStyle(thisTheme.getAppliedStyleResId()); if (style != null) { RenderSessionImpl.getCurrentContext().getRenderResources() .applyStyle(style, themeDelegate.force); diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java index 73d274c..56c0de9 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java @@ -71,7 +71,7 @@ public final class Canvas_Delegate { * Returns the native delegate associated to a given {@link Canvas} object. */ public static Canvas_Delegate getDelegate(Canvas canvas) { - return sManager.getDelegate(canvas.mNativeCanvas); + return sManager.getDelegate(canvas.getNativeCanvas()); } /** @@ -102,7 +102,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static boolean isOpaque(Canvas thisCanvas) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); if (canvasDelegate == null) { return false; } @@ -113,7 +113,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static int getWidth(Canvas thisCanvas) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); if (canvasDelegate == null) { return 0; } @@ -124,7 +124,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static int getHeight(Canvas thisCanvas) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); if (canvasDelegate == null) { return 0; } @@ -135,7 +135,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static void translate(Canvas thisCanvas, float dx, float dy) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); if (canvasDelegate == null) { return; } @@ -146,7 +146,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static void rotate(Canvas thisCanvas, float degrees) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); if (canvasDelegate == null) { return; } @@ -157,7 +157,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static void scale(Canvas thisCanvas, float sx, float sy) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); if (canvasDelegate == null) { return; } @@ -168,7 +168,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static void skew(Canvas thisCanvas, float kx, float ky) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); if (canvasDelegate == null) { return; } @@ -204,7 +204,7 @@ public final class Canvas_Delegate { /*package*/ static boolean clipRect(Canvas thisCanvas, float left, float top, float right, float bottom) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); if (canvasDelegate == null) { return false; } @@ -227,7 +227,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static int save(Canvas thisCanvas, int saveFlags) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); if (canvasDelegate == null) { return 0; } @@ -238,7 +238,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static void restore(Canvas thisCanvas) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); if (canvasDelegate == null) { return; } @@ -249,7 +249,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static int getSaveCount(Canvas thisCanvas) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); if (canvasDelegate == null) { return 0; } @@ -260,7 +260,7 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static void restoreToCount(Canvas thisCanvas, int saveCount) { // get the delegate from the native int. - Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas); + Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.getNativeCanvas()); if (canvasDelegate == null) { return; } @@ -287,7 +287,7 @@ public final class Canvas_Delegate { /*package*/ static void drawLines(Canvas thisCanvas, final float[] pts, final int offset, final int count, Paint paint) { - draw(thisCanvas.mNativeCanvas, paint.mNativePaint, false /*compositeOnly*/, + draw(thisCanvas.getNativeCanvas(), paint.mNativePaint, false /*compositeOnly*/, false /*forceSrcMode*/, new GcSnapshot.Drawable() { @Override public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) { @@ -344,7 +344,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static long native_saveLayer(long nativeCanvas, RectF bounds, + /*package*/ static int native_saveLayer(long nativeCanvas, RectF bounds, long paint, int layerFlags) { // get the delegate from the native int. Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas); @@ -361,7 +361,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static long native_saveLayer(long nativeCanvas, float l, + /*package*/ static int native_saveLayer(long nativeCanvas, float l, float t, float r, float b, long paint, int layerFlags) { // get the delegate from the native int. @@ -380,7 +380,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static long native_saveLayerAlpha(long nativeCanvas, + /*package*/ static int native_saveLayerAlpha(long nativeCanvas, RectF bounds, int alpha, int layerFlags) { // get the delegate from the native int. @@ -393,7 +393,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static long native_saveLayerAlpha(long nativeCanvas, float l, + /*package*/ static int native_saveLayerAlpha(long nativeCanvas, float l, float t, float r, float b, int alpha, int layerFlags) { // get the delegate from the native int. @@ -757,7 +757,8 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static void native_drawRoundRect(long nativeCanvas, - final RectF rect, final float rx, final float ry, long paint) { + final float left, final float top, final float right, final float bottom, + final float rx, final float ry, long paint) { draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/, new GcSnapshot.Drawable() { @@ -769,16 +770,16 @@ public final class Canvas_Delegate { if (style == Paint.Style.FILL.nativeInt || style == Paint.Style.FILL_AND_STROKE.nativeInt) { graphics.fillRoundRect( - (int)rect.left, (int)rect.top, - (int)rect.width(), (int)rect.height(), + (int)left, (int)top, + (int)(right - left), (int)(bottom - top), (int)rx, (int)ry); } if (style == Paint.Style.STROKE.nativeInt || style == Paint.Style.FILL_AND_STROKE.nativeInt) { graphics.drawRoundRect( - (int)rect.left, (int)rect.top, - (int)rect.width(), (int)rect.height(), + (int)left, (int)top, + (int)(right - left), (int)(bottom - top), (int)rx, (int)ry); } } @@ -975,8 +976,10 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static void native_drawText(long nativeCanvas, final char[] text, final int index, final int count, - final float startX, final float startY, final int flags, long paint) { + final float startX, final float startY, final int flags, long paint, + long typeface) { + // TODO: use typeface. draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/, new GcSnapshot.Drawable() { @Override @@ -1006,30 +1009,31 @@ public final class Canvas_Delegate { @LayoutlibDelegate /*package*/ static void native_drawText(long nativeCanvas, String text, - int start, int end, float x, float y, final int flags, long paint) { + int start, int end, float x, float y, final int flags, long paint, + long typeface) { int count = end - start; char[] buffer = TemporaryBuffer.obtain(count); TextUtils.getChars(text, start, end, buffer, 0); - native_drawText(nativeCanvas, buffer, 0, count, x, y, flags, paint); + native_drawText(nativeCanvas, buffer, 0, count, x, y, flags, paint, typeface); } @LayoutlibDelegate /*package*/ static void native_drawTextRun(long nativeCanvas, String text, int start, int end, int contextStart, int contextEnd, - float x, float y, int flags, long paint) { + float x, float y, int flags, long paint, long typeface) { int count = end - start; char[] buffer = TemporaryBuffer.obtain(count); TextUtils.getChars(text, start, end, buffer, 0); - native_drawText(nativeCanvas, buffer, 0, count, x, y, flags, paint); + native_drawText(nativeCanvas, buffer, 0, count, x, y, flags, paint, typeface); } @LayoutlibDelegate /*package*/ static void native_drawTextRun(long nativeCanvas, char[] text, int start, int count, int contextStart, int contextCount, - float x, float y, int flags, long paint) { - native_drawText(nativeCanvas, text, start, count, x, y, flags, paint); + float x, float y, int flags, long paint, long typeface) { + native_drawText(nativeCanvas, text, start, count, x, y, flags, paint, typeface); } @LayoutlibDelegate diff --git a/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java index d6b3da1..bf03a5e 100644 --- a/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java @@ -56,7 +56,7 @@ public abstract class ColorFilter_Delegate { // ---- native methods ---- @LayoutlibDelegate - /*package*/ static void finalizer(long native_instance, long nativeColorFilter) { + /*package*/ static void destroyFilter(long native_instance) { sManager.removeJavaReferenceFor(native_instance); } diff --git a/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java index ca8f450..9aac2bd 100644 --- a/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java @@ -60,11 +60,5 @@ public class ColorMatrixColorFilter_Delegate extends ColorFilter_Delegate { return sManager.addNewDelegate(newDelegate); } - @LayoutlibDelegate - /*package*/ static long nColorMatrixFilter(long nativeFilter, float[] array) { - // pass - return 0; - } - // ---- Private delegate/helper methods ---- } diff --git a/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java index defaac3..501d55c 100644 --- a/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java @@ -60,11 +60,5 @@ public class LightingColorFilter_Delegate extends ColorFilter_Delegate { return sManager.addNewDelegate(newDelegate); } - @LayoutlibDelegate - /*package*/ static int nCreateLightingFilter(long nativeFilter, int mul, int add) { - // pass - return 0; - } - // ---- Private delegate/helper methods ---- } diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java index ebfe9bc..8862f5b 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java @@ -355,227 +355,162 @@ public final class Matrix_Delegate { } @LayoutlibDelegate - /*package*/ static boolean native_setConcat(long native_object, long a, long b) { + /*package*/ static void native_setConcat(long native_object, long a, long b) { if (a == native_object) { - return native_preConcat(native_object, b); + native_preConcat(native_object, b); + return; } else if (b == native_object) { - return native_postConcat(native_object, a); + native_postConcat(native_object, a); + return; } Matrix_Delegate d = sManager.getDelegate(native_object); - if (d == null) { - return false; - } - Matrix_Delegate a_mtx = sManager.getDelegate(a); - if (a_mtx == null) { - return false; - } - Matrix_Delegate b_mtx = sManager.getDelegate(b); - if (b_mtx == null) { - return false; + if (d != null && a_mtx != null && b_mtx != null) { + multiply(d.mValues, a_mtx.mValues, b_mtx.mValues); } - - multiply(d.mValues, a_mtx.mValues, b_mtx.mValues); - - return true; } @LayoutlibDelegate - /*package*/ static boolean native_preTranslate(long native_object, float dx, float dy) { + /*package*/ static void native_preTranslate(long native_object, float dx, float dy) { Matrix_Delegate d = sManager.getDelegate(native_object); - if (d == null) { - return false; + if (d != null) { + d.preTransform(getTranslate(dx, dy)); } - - d.preTransform(getTranslate(dx, dy)); - return true; } @LayoutlibDelegate - /*package*/ static boolean native_preScale(long native_object, float sx, float sy, + /*package*/ static void native_preScale(long native_object, float sx, float sy, float px, float py) { Matrix_Delegate d = sManager.getDelegate(native_object); - if (d == null) { - return false; + if (d != null) { + d.preTransform(getScale(sx, sy, px, py)); } - - d.preTransform(getScale(sx, sy, px, py)); - return true; } @LayoutlibDelegate - /*package*/ static boolean native_preScale(long native_object, float sx, float sy) { + /*package*/ static void native_preScale(long native_object, float sx, float sy) { Matrix_Delegate d = sManager.getDelegate(native_object); - if (d == null) { - return false; + if (d != null) { + d.preTransform(getScale(sx, sy)); } - - d.preTransform(getScale(sx, sy)); - return true; } @LayoutlibDelegate - /*package*/ static boolean native_preRotate(long native_object, float degrees, + /*package*/ static void native_preRotate(long native_object, float degrees, float px, float py) { Matrix_Delegate d = sManager.getDelegate(native_object); - if (d == null) { - return false; + if (d != null) { + d.preTransform(getRotate(degrees, px, py)); } - - d.preTransform(getRotate(degrees, px, py)); - return true; } @LayoutlibDelegate - /*package*/ static boolean native_preRotate(long native_object, float degrees) { + /*package*/ static void native_preRotate(long native_object, float degrees) { Matrix_Delegate d = sManager.getDelegate(native_object); - if (d == null) { - return false; - } + if (d != null) { - double rad = Math.toRadians(degrees); - float sin = (float)Math.sin(rad); - float cos = (float)Math.cos(rad); + double rad = Math.toRadians(degrees); + float sin = (float) Math.sin(rad); + float cos = (float) Math.cos(rad); - d.preTransform(getRotate(sin, cos)); - return true; + d.preTransform(getRotate(sin, cos)); + } } @LayoutlibDelegate - /*package*/ static boolean native_preSkew(long native_object, float kx, float ky, + /*package*/ static void native_preSkew(long native_object, float kx, float ky, float px, float py) { Matrix_Delegate d = sManager.getDelegate(native_object); - if (d == null) { - return false; + if (d != null) { + d.preTransform(getSkew(kx, ky, px, py)); } - - d.preTransform(getSkew(kx, ky, px, py)); - return true; } @LayoutlibDelegate - /*package*/ static boolean native_preSkew(long native_object, float kx, float ky) { + /*package*/ static void native_preSkew(long native_object, float kx, float ky) { Matrix_Delegate d = sManager.getDelegate(native_object); - if (d == null) { - return false; + if (d != null) { + d.preTransform(getSkew(kx, ky)); } - - d.preTransform(getSkew(kx, ky)); - return true; } @LayoutlibDelegate - /*package*/ static boolean native_preConcat(long native_object, long other_matrix) { + /*package*/ static void native_preConcat(long native_object, long other_matrix) { Matrix_Delegate d = sManager.getDelegate(native_object); - if (d == null) { - return false; - } - Matrix_Delegate other = sManager.getDelegate(other_matrix); - if (other == null) { - return false; + if (d != null && other != null) { + d.preTransform(other.mValues); } - - d.preTransform(other.mValues); - return true; } @LayoutlibDelegate - /*package*/ static boolean native_postTranslate(long native_object, float dx, float dy) { + /*package*/ static void native_postTranslate(long native_object, float dx, float dy) { Matrix_Delegate d = sManager.getDelegate(native_object); - if (d == null) { - return false; + if (d != null) { + d.postTransform(getTranslate(dx, dy)); } - - d.postTransform(getTranslate(dx, dy)); - return true; } @LayoutlibDelegate - /*package*/ static boolean native_postScale(long native_object, float sx, float sy, + /*package*/ static void native_postScale(long native_object, float sx, float sy, float px, float py) { Matrix_Delegate d = sManager.getDelegate(native_object); - if (d == null) { - return false; + if (d != null) { + d.postTransform(getScale(sx, sy, px, py)); } - - d.postTransform(getScale(sx, sy, px, py)); - return true; } @LayoutlibDelegate - /*package*/ static boolean native_postScale(long native_object, float sx, float sy) { + /*package*/ static void native_postScale(long native_object, float sx, float sy) { Matrix_Delegate d = sManager.getDelegate(native_object); - if (d == null) { - return false; + if (d != null) { + d.postTransform(getScale(sx, sy)); } - - d.postTransform(getScale(sx, sy)); - return true; } @LayoutlibDelegate - /*package*/ static boolean native_postRotate(long native_object, float degrees, + /*package*/ static void native_postRotate(long native_object, float degrees, float px, float py) { Matrix_Delegate d = sManager.getDelegate(native_object); - if (d == null) { - return false; + if (d != null) { + d.postTransform(getRotate(degrees, px, py)); } - - d.postTransform(getRotate(degrees, px, py)); - return true; } @LayoutlibDelegate - /*package*/ static boolean native_postRotate(long native_object, float degrees) { + /*package*/ static void native_postRotate(long native_object, float degrees) { Matrix_Delegate d = sManager.getDelegate(native_object); - if (d == null) { - return false; + if (d != null) { + d.postTransform(getRotate(degrees)); } - - d.postTransform(getRotate(degrees)); - return true; } @LayoutlibDelegate - /*package*/ static boolean native_postSkew(long native_object, float kx, float ky, + /*package*/ static void native_postSkew(long native_object, float kx, float ky, float px, float py) { Matrix_Delegate d = sManager.getDelegate(native_object); - if (d == null) { - return false; + if (d != null) { + d.postTransform(getSkew(kx, ky, px, py)); } - - d.postTransform(getSkew(kx, ky, px, py)); - return true; } @LayoutlibDelegate - /*package*/ static boolean native_postSkew(long native_object, float kx, float ky) { + /*package*/ static void native_postSkew(long native_object, float kx, float ky) { Matrix_Delegate d = sManager.getDelegate(native_object); - if (d == null) { - return false; + if (d != null) { + d.postTransform(getSkew(kx, ky)); } - - d.postTransform(getSkew(kx, ky)); - return true; } @LayoutlibDelegate - /*package*/ static boolean native_postConcat(long native_object, long other_matrix) { + /*package*/ static void native_postConcat(long native_object, long other_matrix) { Matrix_Delegate d = sManager.getDelegate(native_object); - if (d == null) { - return false; - } - Matrix_Delegate other = sManager.getDelegate(other_matrix); - if (other == null) { - return false; + if (d != null && other != null) { + d.postTransform(other.mValues); } - - d.postTransform(other.mValues); - return true; } @LayoutlibDelegate diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java index 7007b71..83df745 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java @@ -420,7 +420,7 @@ public class Paint_Delegate { } @LayoutlibDelegate - /*package*/ static void nSetShadowLayer(Paint thisPaint, float radius, float dx, float dy, + /*package*/ static void native_setShadowLayer(long paint, float radius, float dx, float dy, int color) { // FIXME Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, @@ -428,6 +428,24 @@ public class Paint_Delegate { } @LayoutlibDelegate + /*package*/ static boolean native_hasShadowLayer(long paint) { + // FIXME + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Paint.hasShadowLayer is not supported.", null, null /*data*/); + return false; + } + + @LayoutlibDelegate + /*package*/ static boolean isElegantTextHeight(Paint thisPaint) { + return false; + } + + @LayoutlibDelegate + /*package*/ static void setElegantTextHeight(Paint thisPaint, boolean elegant) { + // TODO + } + + @LayoutlibDelegate /*package*/ static float getTextSize(Paint thisPaint) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); @@ -688,7 +706,7 @@ public class Paint_Delegate { } @LayoutlibDelegate - /*package*/ static long native_getStyle(long native_object) { + /*package*/ static int native_getStyle(long native_object) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(native_object); if (delegate == null) { @@ -710,7 +728,7 @@ public class Paint_Delegate { } @LayoutlibDelegate - /*package*/ static long native_getStrokeCap(long native_object) { + /*package*/ static int native_getStrokeCap(long native_object) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(native_object); if (delegate == null) { @@ -732,7 +750,7 @@ public class Paint_Delegate { } @LayoutlibDelegate - /*package*/ static long native_getStrokeJoin(long native_object) { + /*package*/ static int native_getStrokeJoin(long native_object) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(native_object); if (delegate == null) { @@ -889,7 +907,7 @@ public class Paint_Delegate { } @LayoutlibDelegate - /*package*/ static long native_getTextAlign(long native_object) { + /*package*/ static int native_getTextAlign(long native_object) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(native_object); if (delegate == null) { @@ -922,7 +940,7 @@ public class Paint_Delegate { } @LayoutlibDelegate - /*package*/ static long native_getTextWidths(long native_object, char[] text, int index, + /*package*/ static int native_getTextWidths(long native_object, char[] text, int index, int count, int bidiFlags, float[] widths) { // get the delegate from the native int. Paint_Delegate delegate = sManager.getDelegate(native_object); @@ -964,14 +982,14 @@ public class Paint_Delegate { } @LayoutlibDelegate - /*package*/ static long native_getTextWidths(long native_object, String text, int start, + /*package*/ static int native_getTextWidths(long native_object, String text, int start, int end, int bidiFlags, float[] widths) { return native_getTextWidths(native_object, text.toCharArray(), start, end - start, bidiFlags, widths); } @LayoutlibDelegate - /* package */static long native_getTextGlyphs(long native_object, String text, int start, + /* package */static int native_getTextGlyphs(long native_object, String text, int start, int end, int contextStart, int contextEnd, int flags, char[] glyphs) { // FIXME return 0; @@ -1012,7 +1030,7 @@ public class Paint_Delegate { } @LayoutlibDelegate - /*package*/ static long native_getTextRunCursor(Paint thisPaint, long native_object, char[] text, + /*package*/ static int native_getTextRunCursor(Paint thisPaint, long native_object, char[] text, int contextStart, int contextLength, int flags, int offset, int cursorOpt) { // FIXME Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, @@ -1021,7 +1039,7 @@ public class Paint_Delegate { } @LayoutlibDelegate - /*package*/ static long native_getTextRunCursor(Paint thisPaint, long native_object, String text, + /*package*/ static int native_getTextRunCursor(Paint thisPaint, long native_object, String text, int contextStart, int contextEnd, int flags, int offset, int cursorOpt) { // FIXME Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java index 6f6ef20..0ec7115 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java @@ -142,7 +142,14 @@ public final class Path_Delegate { } @LayoutlibDelegate - /*package*/ static long native_getFillType(long nPath) { + /*package*/ static boolean native_isConvex(long nPath) { + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Path.isConvex is not supported.", null, null); + return true; + } + + @LayoutlibDelegate + /*package*/ static int native_getFillType(long nPath) { Path_Delegate pathDelegate = sManager.getDelegate(nPath); if (pathDelegate == null) { return 0; @@ -311,16 +318,6 @@ public final class Path_Delegate { } @LayoutlibDelegate - /*package*/ static void native_addRect(long nPath, RectF rect, int dir) { - Path_Delegate pathDelegate = sManager.getDelegate(nPath); - if (pathDelegate == null) { - return; - } - - pathDelegate.addRect(rect.left, rect.top, rect.right, rect.bottom, dir); - } - - @LayoutlibDelegate /*package*/ static void native_addRect(long nPath, float left, float top, float right, float bottom, int dir) { Path_Delegate pathDelegate = sManager.getDelegate(nPath); @@ -332,14 +329,15 @@ public final class Path_Delegate { } @LayoutlibDelegate - /*package*/ static void native_addOval(long nPath, RectF oval, int dir) { + /*package*/ static void native_addOval(long nPath, float left, float top, float right, + float bottom, int dir) { Path_Delegate pathDelegate = sManager.getDelegate(nPath); if (pathDelegate == null) { return; } pathDelegate.mPath.append(new Ellipse2D.Float( - oval.left, oval.top, oval.width(), oval.height()), false); + left, top, right - left, bottom - top), false); } @LayoutlibDelegate @@ -484,6 +482,11 @@ public final class Path_Delegate { sManager.removeJavaReferenceFor(nPath); } + @LayoutlibDelegate + /*package*/ static float[] native_approximate(long nPath, float error) { + Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, "Path.approximate() not supported", null); + return new float[0]; + } // ---- Private helper methods ---- diff --git a/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java index 6049919..1bc3033 100644 --- a/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java @@ -60,12 +60,5 @@ public class PorterDuffColorFilter_Delegate extends ColorFilter_Delegate { return sManager.addNewDelegate(newDelegate); } - @LayoutlibDelegate - /*package*/ static long nCreatePorterDuffFilter(long nativeFilter, int srcColor, - int porterDuffMode) { - // pass - return 0; - } - // ---- Private delegate/helper methods ---- } diff --git a/tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java index d2aae92..edb7025 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java @@ -289,7 +289,6 @@ public class Region_Delegate { dstRegion.mArea.reset(); dstRegion.mArea.add(srcRegion.mArea); - return; } @LayoutlibDelegate diff --git a/tools/layoutlib/bridge/src/android/os/Build_Delegate.java b/tools/layoutlib/bridge/src/android/os/SystemProperties_Delegate.java index ff82a5e..1e7564e 100644 --- a/tools/layoutlib/bridge/src/android/os/Build_Delegate.java +++ b/tools/layoutlib/bridge/src/android/os/SystemProperties_Delegate.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * Copyright (C) 2014 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. @@ -23,26 +23,29 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import java.util.Map; /** - * Delegate implementing the native methods of android.os.Build + * Delegate implementing the native methods of android.os.SystemProperties * - * Through the layoutlib_create tool, the original native methods of Build have been replaced - * by calls to methods of the same name in this delegate class. + * Through the layoutlib_create tool, the original native methods of SystemProperties have been + * replaced by calls to methods of the same name in this delegate class. * * Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager} * around to map int to instance of the delegate. - * */ -public class Build_Delegate { +public class SystemProperties_Delegate { @LayoutlibDelegate - /*package*/ static String getString(String property) { + /*package*/ static String native_get(String key) { + return native_get(key, ""); + } + + @LayoutlibDelegate + /*package*/ static String native_get(String key, String def) { Map<String, String> properties = Bridge.getPlatformProperties(); - String value = properties.get(property); + String value = properties.get(key); if (value != null) { return value; } - return Build.UNKNOWN; + return def; } - } diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java index a2e93a7..af22f44 100644 --- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java +++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java @@ -121,10 +121,11 @@ public final class BridgeInflater extends LayoutInflater { } @Override - public View createViewFromTag(View parent, String name, AttributeSet attrs) { + public View createViewFromTag(View parent, String name, AttributeSet attrs, + boolean inheritContext) { View view = null; try { - view = super.createViewFromTag(parent, name, attrs); + view = super.createViewFromTag(parent, name, attrs, inheritContext); } catch (InflateException e) { // try to load the class from using the custom view loader try { diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java index dd2cbc1..757cdd2 100644 --- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java +++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java @@ -23,22 +23,13 @@ import com.android.internal.view.IInputMethodClient; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.Bitmap; -import android.graphics.Rect; import android.os.Bundle; import android.os.IBinder; import android.os.IRemoteCallback; import android.os.RemoteException; import android.util.DisplayMetrics; -import android.view.Display; -import android.view.Gravity; -import android.view.IApplicationToken; -import android.view.IInputFilter; -import android.view.IOnKeyguardExitResult; -import android.view.IRotationWatcher; -import android.view.IWindowManager; -import android.view.IWindowSession; -import java.util.List; +import java.lang.Override; /** * Basic implementation of {@link IWindowManager} so that {@link Display} (and @@ -458,44 +449,26 @@ public class IWindowManagerImpl implements IWindowManager { } @Override - public IBinder getFocusedWindowToken() { - // TODO Auto-generated method stub - return null; - } - - @Override - public void setInputFilter(IInputFilter filter) throws RemoteException { - // TODO Auto-generated method stub - } - - @Override - public void getWindowFrame(IBinder token, Rect outFrame) { + public boolean isRotationFrozen() throws RemoteException { // TODO Auto-generated method stub + return false; } @Override - public void setMagnificationCallbacks(IMagnificationCallbacks callbacks) { + public void enableScreenIfNeeded() throws RemoteException { // TODO Auto-generated method stub } @Override - public void setMagnificationSpec(MagnificationSpec spec) { + public boolean clearWindowContentFrameStats(IBinder token) throws RemoteException { // TODO Auto-generated method stub + return false; } @Override - public MagnificationSpec getCompatibleMagnificationSpecForWindow(IBinder windowToken) { + public WindowContentFrameStats getWindowContentFrameStats(IBinder token) + throws RemoteException { // TODO Auto-generated method stub return null; } - - @Override - public boolean isRotationFrozen() throws RemoteException { - // TODO Auto-generated method stub - return false; - } - - @Override - public void setTouchExplorationEnabled(boolean enabled) { - } } diff --git a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java index 3db3a1b..7a73fae 100644 --- a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java +++ b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java @@ -48,9 +48,9 @@ public class LayoutInflater_Delegate { * This implementation just records the merge status before calling the default implementation. */ @LayoutlibDelegate - /*package*/ static void rInflate(LayoutInflater thisInflater, - XmlPullParser parser, View parent, final AttributeSet attrs, - boolean finishInflate) throws XmlPullParserException, IOException { + /* package */ static void rInflate(LayoutInflater thisInflater, XmlPullParser parser, + View parent, final AttributeSet attrs, boolean finishInflate, boolean inheritContext) + throws XmlPullParserException, IOException { if (finishInflate == false) { // this is a merge rInflate! @@ -61,7 +61,7 @@ public class LayoutInflater_Delegate { // ---- START DEFAULT IMPLEMENTATION. - thisInflater.rInflate_Original(parser, parent, attrs, finishInflate); + thisInflater.rInflate_Original(parser, parent, attrs, finishInflate, inheritContext); // ---- END DEFAULT IMPLEMENTATION. @@ -74,10 +74,8 @@ public class LayoutInflater_Delegate { } @LayoutlibDelegate - public static void parseInclude( - LayoutInflater thisInflater, - XmlPullParser parser, View parent, AttributeSet attrs) - throws XmlPullParserException, IOException { + public static void parseInclude(LayoutInflater thisInflater, XmlPullParser parser, View parent, + AttributeSet attrs, boolean inheritContext) throws XmlPullParserException, IOException { int type; @@ -113,9 +111,11 @@ public class LayoutInflater_Delegate { if (TAG_MERGE.equals(childName)) { // Inflate all children. - thisInflater.rInflate(childParser, parent, childAttrs, false); + thisInflater.rInflate(childParser, parent, childAttrs, false, + inheritContext); } else { - final View view = thisInflater.createViewFromTag(parent, childName, childAttrs); + final View view = thisInflater.createViewFromTag(parent, childName, + childAttrs, inheritContext); final ViewGroup group = (ViewGroup) parent; // We try to load the layout params set in the <include /> tag. If @@ -151,7 +151,7 @@ public class LayoutInflater_Delegate { } // Inflate all children. - thisInflater.rInflate(childParser, view, childAttrs, true); + thisInflater.rInflate(childParser, view, childAttrs, true, true); // Attempt to override the included layout's android:id with the // one set on the <include /> tag itself. diff --git a/tools/layoutlib/bridge/src/android/view/MenuInflater_Delegate.java b/tools/layoutlib/bridge/src/android/view/MenuInflater_Delegate.java index e34ad38..0dddf3d 100644 --- a/tools/layoutlib/bridge/src/android/view/MenuInflater_Delegate.java +++ b/tools/layoutlib/bridge/src/android/view/MenuInflater_Delegate.java @@ -23,7 +23,6 @@ import com.android.internal.view.menu.BridgeMenuItemImpl; import com.android.internal.view.menu.MenuView; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.android.BridgeContext; -import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import android.util.AttributeSet; diff --git a/tools/layoutlib/bridge/src/com/android/internal/view/menu/MenuBuilderAccessor.java b/tools/layoutlib/bridge/src/com/android/internal/view/menu/MenuBuilderAccessor.java deleted file mode 100644 index f0798cb..0000000 --- a/tools/layoutlib/bridge/src/com/android/internal/view/menu/MenuBuilderAccessor.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.android.internal.view.menu; - -import java.util.ArrayList; - -/** - * To access non public members of {@link MenuBuilder} - */ -public class MenuBuilderAccessor { - public static ArrayList<MenuItemImpl> getNonActionItems(MenuBuilder builder) { - return builder.getNonActionItems(); - } -} diff --git a/tools/layoutlib/bridge/src/com/android/internal/widget/ActionBarAccessor.java b/tools/layoutlib/bridge/src/com/android/internal/widget/ActionBarAccessor.java index 47a3679..40b6220 100644 --- a/tools/layoutlib/bridge/src/com/android/internal/widget/ActionBarAccessor.java +++ b/tools/layoutlib/bridge/src/com/android/internal/widget/ActionBarAccessor.java @@ -16,7 +16,7 @@ package com.android.internal.widget; -import com.android.internal.view.menu.ActionMenuPresenter; +import android.widget.ActionMenuPresenter; /** * To access non public members of AbsActionBarView diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java index 01740b1..89288bf 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java @@ -135,6 +135,7 @@ public final class BridgeContentProvider implements IContentProvider { return null; } + @Override public Uri canonicalize(String callingPkg, Uri uri) throws RemoteException { return null; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index 6595ce1..d31239b 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java @@ -58,6 +58,7 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; +import android.hardware.display.DisplayManager; import android.net.Uri; import android.os.Bundle; import android.os.Handler; @@ -101,6 +102,7 @@ public final class BridgeContext extends Context { private final ApplicationInfo mApplicationInfo; private final IProjectCallback mProjectCallback; private final WindowManager mWindowManager; + private final DisplayManager mDisplayManager; private Resources.Theme mTheme; @@ -149,6 +151,7 @@ public final class BridgeContext extends Context { } mWindowManager = new WindowManagerImpl(mMetrics); + mDisplayManager = new DisplayManager(this); } /** @@ -455,6 +458,10 @@ public final class BridgeContext extends Context { return new PowerManager(this, new BridgePowerManager(), new Handler()); } + if (DISPLAY_SERVICE.equals(service)) { + return mDisplayManager; + } + throw new UnsupportedOperationException("Unsupported Service: " + service); } @@ -560,7 +567,7 @@ public final class BridgeContext extends Context { StyleResourceValue customStyleValues = null; if (customStyle != null) { ResourceValue item = mRenderResources.findResValue(customStyle, - false /*forceFrameworkOnly*/); + isPlatformFile /*forceFrameworkOnly*/); // resolve it in case it links to something else item = mRenderResources.resolveResValue(item); @@ -1277,6 +1284,14 @@ public final class BridgeContext extends Context { } @Override + public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user, + String receiverPermission, int appOp, BroadcastReceiver resultReceiver, + Handler scheduler, + int initialCode, String initialData, Bundle initialExtras) { + // pass + } + + @Override public void sendStickyBroadcast(Intent arg0) { // pass diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java index 3cf5ed5..0bb7fc2 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java @@ -209,6 +209,17 @@ public class BridgeIInputMethodManager implements IInputMethodManager { } @Override + public int getInputMethodWindowVisibleHeight() throws RemoteException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public void notifyTextCommitted() throws RemoteException { + // TODO Auto-generated method stub + } + + @Override public void updateStatusIcon(IBinder arg0, String arg1, int arg2) throws RemoteException { // TODO Auto-generated method stub @@ -227,4 +238,9 @@ public class BridgeIInputMethodManager implements IInputMethodManager { // TODO Auto-generated method stub return null; } + + @Override + public void setCursorAnchorMonitorMode(IBinder arg0, int arg1) { + // TODO Auto-generated method stub + } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java index 95221fb..00c0f93 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java @@ -39,7 +39,7 @@ public class BridgePowerManager implements IPowerManager { } @Override - public void acquireWakeLock(IBinder arg0, int arg1, String arg2, String arg2_5, WorkSource arg3) + public void acquireWakeLock(IBinder arg0, int arg1, String arg2, String arg2_5, WorkSource arg3, String arg4) throws RemoteException { // pass for now. } @@ -51,6 +51,11 @@ public class BridgePowerManager implements IPowerManager { } @Override + public void powerHint(int hintId, int data) { + // pass for now. + } + + @Override public void crash(String arg0) throws RemoteException { // pass for now. } @@ -111,7 +116,7 @@ public class BridgePowerManager implements IPowerManager { } @Override - public void updateWakeLockWorkSource(IBinder arg0, WorkSource arg1) throws RemoteException { + public void updateWakeLockWorkSource(IBinder arg0, WorkSource arg1, String arg2) throws RemoteException { // pass for now. } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java index 0e39a57..936ab4f 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java @@ -25,11 +25,9 @@ import com.android.ide.common.rendering.api.ResourceValue; import com.android.ide.common.rendering.api.SessionParams; import com.android.ide.common.rendering.api.SystemViewCookie; import com.android.internal.R; -import com.android.internal.app.ActionBarImpl; +import com.android.internal.app.WindowDecorActionBar; import com.android.internal.util.Predicate; -import com.android.internal.view.menu.ActionMenuView; import com.android.internal.view.menu.MenuBuilder; -import com.android.internal.view.menu.MenuBuilderAccessor; import com.android.internal.view.menu.MenuItemImpl; import com.android.internal.widget.ActionBarAccessor; import com.android.internal.widget.ActionBarContainer; @@ -52,6 +50,7 @@ import android.view.LayoutInflater; import android.view.MenuInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.ActionMenuView; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.ListAdapter; @@ -101,7 +100,7 @@ public class ActionBarLayout extends LinearLayout { // Inflate action bar layout. LayoutInflater.from(context).inflate(R.layout.screen_action_bar, this, true /*attachToRoot*/); - mActionBar = new ActionBarImpl(this); + mActionBar = new WindowDecorActionBar(this); // Set contexts. mBridgeContext = context; @@ -323,7 +322,7 @@ public class ActionBarLayout extends LinearLayout { return false; } // Copied from android.widget.ActionMenuPresenter.updateMenuView() - ArrayList<MenuItemImpl> menus = MenuBuilderAccessor.getNonActionItems(mMenuBuilder); + ArrayList<MenuItemImpl> menus = mMenuBuilder.getNonActionItems(); if (ActionBarAccessor.getActionMenuPresenter(mActionBarView).isOverflowReserved() && menus != null) { final int count = menus.size(); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java index bcd08eb4..86797e5 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java @@ -87,7 +87,7 @@ abstract class CustomBar extends LinearLayout { } } - private InputStream getIcon(String iconName, Density[] densityInOut, LayoutDirection direction, + private InputStream getIcon(String iconName, Density[] densityInOut, LayoutDirection direction, String[] pathOut, boolean tryOtherDensities) { // current density Density density = densityInOut[0]; @@ -111,10 +111,10 @@ abstract class CustomBar extends LinearLayout { return stream; } } - } - // couldn't find resource with direction qualifier. try without. - if (direction != null) { - return getIcon(iconName, densityInOut, null, pathOut, true); + // couldn't find resource with direction qualifier. try without. + if (direction != null) { + return getIcon(iconName, densityInOut, null, pathOut, true); + } } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java index 84e676e..112c267 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java @@ -17,7 +17,6 @@ package com.android.layoutlib.bridge.bars; import com.android.resources.Density; -import com.android.layoutlib.bridge.Bridge; import org.xmlpull.v1.XmlPullParserException; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/OverflowMenuAdapter.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/OverflowMenuAdapter.java index 79e231c..778305d 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/OverflowMenuAdapter.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/OverflowMenuAdapter.java @@ -17,7 +17,6 @@ package com.android.layoutlib.bridge.bars; import com.android.internal.view.menu.MenuBuilder; -import com.android.internal.view.menu.MenuBuilderAccessor; import com.android.internal.view.menu.MenuItemImpl; import com.android.internal.view.menu.MenuView; @@ -47,7 +46,7 @@ public class OverflowMenuAdapter extends BaseAdapter { @Override public int getCount() { - ArrayList<MenuItemImpl> items = MenuBuilderAccessor.getNonActionItems(mMenu); + ArrayList<MenuItemImpl> items = mMenu.getNonActionItems(); if (mExpandedIndex < 0) { return items.size(); } @@ -56,7 +55,7 @@ public class OverflowMenuAdapter extends BaseAdapter { @Override public MenuItemImpl getItem(int position) { - ArrayList<MenuItemImpl> items = MenuBuilderAccessor.getNonActionItems(mMenu); + ArrayList<MenuItemImpl> items = mMenu.getNonActionItems(); if (mExpandedIndex >= 0 && position >= mExpandedIndex) { position++; } @@ -86,7 +85,7 @@ public class OverflowMenuAdapter extends BaseAdapter { private void findExpandedIndex() { final MenuItemImpl expandedItem = mMenu.getExpandedItem(); if (expandedItem != null) { - final ArrayList<MenuItemImpl> items = MenuBuilderAccessor.getNonActionItems(mMenu); + final ArrayList<MenuItemImpl> items = mMenu.getNonActionItems(); final int count = items.size(); for (int i = 0; i < count; i++) { final MenuItemImpl item = items.get(i); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java index 3692d96..1498044 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java @@ -33,7 +33,6 @@ public class StatusBar extends CustomBar { public StatusBar(Context context, Density density, int direction, boolean RtlEnabled) throws XmlPullParserException { // FIXME: if direction is RTL but it's not enabled in application manifest, mirror this bar. - super(context, density, LinearLayout.HORIZONTAL, "/bars/status_bar.xml", "status_bar.xml"); // FIXME: use FILL_H? diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/SparseWeakArray.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/SparseWeakArray.java index 53e1640..a2a8aa9 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/SparseWeakArray.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/SparseWeakArray.java @@ -18,6 +18,7 @@ package com.android.layoutlib.bridge.util; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.GrowingArrayUtils; import android.util.SparseArray; @@ -59,10 +60,8 @@ public class SparseWeakArray<E> { * number of mappings. */ public SparseWeakArray(int initialCapacity) { - initialCapacity = ArrayUtils.idealLongArraySize(initialCapacity); - - mKeys = new long[initialCapacity]; - mValues = new WeakReference[initialCapacity]; + mKeys = ArrayUtils.newUnpaddedLongArray(initialCapacity); + mValues = new WeakReference[mKeys.length]; mSize = 0; } @@ -142,18 +141,6 @@ public class SparseWeakArray<E> { mGarbage = false; mSize = o; - - int newSize = ArrayUtils.idealLongArraySize(mSize); - if (newSize < mKeys.length) { - long[] nkeys = new long[newSize]; - WeakReference<?>[] nvalues = new WeakReference[newSize]; - - System.arraycopy(mKeys, 0, nkeys, 0, newSize); - System.arraycopy(mValues, 0, nvalues, 0, newSize); - - mKeys = nkeys; - mValues = nvalues; - } } /** @@ -182,28 +169,8 @@ public class SparseWeakArray<E> { i = ~binarySearch(mKeys, 0, mSize, key); } - if (mSize >= mKeys.length) { - int n = ArrayUtils.idealLongArraySize(mSize + 1); - - long[] nkeys = new long[n]; - WeakReference<?>[] nvalues = new WeakReference[n]; - - // Log.e("SparseArray", "grow " + mKeys.length + " to " + n); - System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); - System.arraycopy(mValues, 0, nvalues, 0, mValues.length); - - mKeys = nkeys; - mValues = nvalues; - } - - if (mSize - i != 0) { - // Log.e("SparseArray", "move " + (mSize - i)); - System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i); - System.arraycopy(mValues, i, mValues, i + 1, mSize - i); - } - - mKeys[i] = key; - mValues[i] = new WeakReference(value); + mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key); + mValues = GrowingArrayUtils.insert(mValues, mSize, i, new WeakReference(value)); mSize++; } } @@ -321,24 +288,9 @@ public class SparseWeakArray<E> { gc(); } - int pos = mSize; - if (pos >= mKeys.length) { - int n = ArrayUtils.idealLongArraySize(pos + 1); - - long[] nkeys = new long[n]; - WeakReference<?>[] nvalues = new WeakReference[n]; - - // Log.e("SparseArray", "grow " + mKeys.length + " to " + n); - System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); - System.arraycopy(mValues, 0, nvalues, 0, mValues.length); - - mKeys = nkeys; - mValues = nvalues; - } - - mKeys[pos] = key; - mValues[pos] = new WeakReference(value); - mSize = pos + 1; + mKeys = GrowingArrayUtils.append(mKeys, mSize, key); + mValues = GrowingArrayUtils.append(mValues, mSize, new WeakReference(value)); + mSize++; } private boolean hasReclaimedRefs() { diff --git a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java index 0f66fd7..19d249b 100644 --- a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java +++ b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java @@ -18,6 +18,7 @@ package libcore.icu; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import com.ibm.icu.text.DateTimePatternGenerator; +import com.ibm.icu.util.Currency; import com.ibm.icu.util.ULocale; import java.util.Locale; @@ -117,6 +118,11 @@ public class ICU_Delegate { } @LayoutlibDelegate + /*package*/ static int getCurrencyNumericCode(String currencyCode) { + return Currency.getInstance(currencyCode).getNumericCode(); + } + + @LayoutlibDelegate /*package*/ static String getCurrencySymbol(String locale, String currencyCode) { return ""; } diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java index 7c0bc84..274516c 100644 --- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java +++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java @@ -121,14 +121,6 @@ public class TestDelegates extends TestCase { Method delegateMethod = delegateClass.getDeclaredMethod(originalMethod.getName(), parameters); - // check that the method has the annotation - assertNotNull( - String.format( - "Delegate method %1$s for class %2$s does not have the @LayoutlibDelegate annotation", - delegateMethod.getName(), - originalClass.getName()), - delegateMethod.getAnnotation(LayoutlibDelegate.class)); - // check the return type of the methods match. assertTrue( String.format("Delegate method %1$s.%2$s does not match the corresponding " + @@ -138,6 +130,14 @@ public class TestDelegates extends TestCase { originalMethod.getReturnType().getName()), delegateMethod.getReturnType() == originalMethod.getReturnType()); + // check that the method has the annotation + assertNotNull( + String.format( + "Delegate method %1$s for class %2$s does not have the @LayoutlibDelegate annotation", + delegateMethod.getName(), + originalClass.getName()), + delegateMethod.getAnnotation(LayoutlibDelegate.class)); + // check that the method is static assertTrue( String.format( diff --git a/tools/layoutlib/create/.classpath b/tools/layoutlib/create/.classpath index cd8bb0d..25c3b3e 100644 --- a/tools/layoutlib/create/.classpath +++ b/tools/layoutlib/create/.classpath @@ -4,6 +4,6 @@ <classpathentry excluding="mock_data/" kind="src" path="tests"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/asm-tools/asm-4.0.jar" sourcepath="/ANDROID_PLAT/prebuilts/tools/common/asm-tools/src-4.0.zip"/> + <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/asm/asm-4.0.jar" sourcepath="/ANDROID_PLAT_SRC/prebuilts/misc/common/asm/src.zip"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/tools/layoutlib/create/README.txt b/tools/layoutlib/create/README.txt index ef2b185..6e0a300 100644 --- a/tools/layoutlib/create/README.txt +++ b/tools/layoutlib/create/README.txt @@ -4,46 +4,45 @@ - Description - --------------- -Layoutlib_create generates a JAR library used by the Eclipse graphical layout editor -to perform layout. +Layoutlib_create generates a JAR library used by the Eclipse graphical layout editor to perform +layout. - Usage - --------- - ./layoutlib_create path/to/android.jar destination.jar + ./layoutlib_create destination.jar path/to/android1.jar path/to/android2.jar - Design Overview - ------------------- -Layoutlib_create uses the "android.jar" containing all the Java code used by Android -as generated by the Android build, right before the classes are converted to a DEX format. +Layoutlib_create uses a few jars from the framework containing the Java code used by Android as +generated by the Android build, right before the classes are converted to a DEX format. -The Android JAR can't be used directly in Eclipse: -- it contains references to native code (which we want to avoid in Eclipse), -- some classes need to be overridden, for example all the drawing code that is - replaced by Java 2D calls in Eclipse. -- some of the classes that need to be changed are final and/or we need access - to their private internal state. +These jars can't be used directly in Eclipse as: +- they contains references to native code (which we want to avoid in Eclipse), +- some classes need to be overridden, for example all the drawing code that is replaced by Java 2D + calls in Eclipse. +- some of the classes that need to be changed are final and/or we need access to their private + internal state. Consequently this tool: - parses the input JAR, - modifies some of the classes directly using some bytecode manipulation, - filters some packages and removes those we don't want in the output JAR, - injects some new classes, -- generates a modified JAR file that is suitable for the Android plugin - for Eclipse to perform rendering. +- generates a modified JAR file that is suitable for the Android plugin for Eclipse to perform + rendering. The ASM library is used to do the bytecode modification using its visitor pattern API. -The layoutlib_create is *NOT* generic. There is no configuration file. Instead all the -configuration is done in the main() method and the CreateInfo structure is expected to -change with the Android platform as new classes are added, changed or removed. +The layoutlib_create is *NOT* generic. There is no configuration file. Instead all the configuration +is done in the main() method and the CreateInfo structure is expected to change with the Android +platform as new classes are added, changed or removed. -The resulting JAR is used by layoutlib_bridge (a.k.a. "the bridge"), also part of the -platform, that provides all the necessary missing implementation for rendering graphics -in Eclipse. +The resulting JAR is used by layoutlib_bridge (a.k.a. "the bridge"), also part of the platform, that +provides all the necessary missing implementation for rendering graphics in Eclipse. @@ -58,97 +57,96 @@ The tool works in two phases: - Analyzer ---------- -The goal of the analyzer is to create a graph of all the classes from the input JAR -with their dependencies and then only keep the ones we want. +The goal of the analyzer is to create a graph of all the classes from the input JAR with their +dependencies and then only keep the ones we want. -To do that, the analyzer is created with a list of base classes to keep -- everything -that derives from these is kept. Currently the one such class is android.view.View: -since we want to render layouts, anything that is sort of a view needs to be kept. +To do that, the analyzer is created with a list of base classes to keep -- everything that derives +from these is kept. Currently the one such class is android.view.View: since we want to render +layouts, anything that is sort of a view needs to be kept. -The analyzer is also given a list of class names to keep in the output. -This is done using shell-like glob patterns that filter on the fully-qualified -class names, for example "android.*.R**" ("*" does not matches dots whilst "**" does, -and "." and "$" are interpreted as-is). -In practice we almost but not quite request the inclusion of full packages. +The analyzer is also given a list of class names to keep in the output. This is done using +shell-like glob patterns that filter on the fully-qualified class names, for example "android.*.R**" +("*" does not matches dots whilst "**" does, and "." and "$" are interpreted as-is). In practice we +almost but not quite request the inclusion of full packages. -The analyzer is also given a list of classes to exclude. A fake implementation of these -classes is injected by the Generator. +The analyzer is also given a list of classes to exclude. A fake implementation of these classes is +injected by the Generator. -With this information, the analyzer parses the input zip to find all the classes. -All classes deriving from the requested bases classes are kept. -All classes which name matched the glob pattern are kept. -The analysis then finds all the dependencies of the classes that are to be kept -using an ASM visitor on the class, the field types, the method types and annotations types. -Classes that belong to the current JRE are excluded. +With this information, the analyzer parses the input zip to find all the classes. All classes +deriving from the requested bases classes are kept. All classes whose name match the glob pattern +are kept. The analysis then finds all the dependencies of the classes that are to be kept using an +ASM visitor on the class, the field types, the method types and annotations types. Classes that +belong to the current JRE are excluded. -The output of the analyzer is a set of ASM ClassReader instances which are then -fed to the generator. +The output of the analyzer is a set of ASM ClassReader instances which are then fed to the +generator. - Generator ----------- -The generator is constructed from a CreateInfo struct that acts as a config file -and lists: -- the classes to inject in the output JAR -- these classes are directly implemented - in layoutlib_create and will be used to interface with the renderer in Eclipse. +The generator is constructed from a CreateInfo struct that acts as a config file and lists: +- the classes to inject in the output JAR -- these classes are directly implemented in + layoutlib_create and will be used to interface with the renderer in Eclipse. - specific methods to override (see method stubs details below). - specific methods for which to delegate calls. - specific methods to remove based on their return type. - specific classes to rename. - specific classes to refactor. -Each of these are specific strategies we use to be able to modify the Android code -to fit within the Eclipse renderer. These strategies are explained beow. +Each of these are specific strategies we use to be able to modify the Android code to fit within the +Eclipse renderer. These strategies are explained beow. -The core method of the generator is transform(): it takes an input ASM ClassReader -and modifies it to produce a byte array suitable for the final JAR file. +The core method of the generator is transform(): it takes an input ASM ClassReader and modifies it +to produce a byte array suitable for the final JAR file. The first step of the transformation is to implement the method delegates. -The TransformClassAdapter is then used to process the potentially renamed class. -All protected or private classes are market as public. -All classes are made non-final. -Interfaces are left as-is. +The TransformClassAdapter is then used to process the potentially renamed class. All protected or +private classes are market as public. All classes are made non-final. Interfaces are left as-is. -If a method has a return type that must be erased, the whole method is skipped. -Methods are also changed from protected/private to public. -The code of the methods is then kept as-is, except for native methods which are -replaced by a stub. Methods that are to be overridden are also replaced by a stub. +If a method has a return type that must be erased, the whole method is skipped. Methods are also +changed from protected/private to public. The code of the methods is then kept as-is, except for +native methods which are replaced by a stub. Methods that are to be overridden are also replaced by +a stub. Finally fields are also visited and changed from protected/private to public. -The next step of the transformation is changing the name of the class in case -we requested the class to be renamed. This uses the RenameClassAdapter to also rename -all inner classes and references in methods and types. Note that other classes are -not transformed and keep referencing the original name. - -The class is then fed to RefactorClassAdapter which is like RenameClassAdapter but -updates the references in all classes. This is used to update the references of classes -in the java package that were added in the Dalvik VM but are not a part of the standard -JVM. The existing classes are modified to update all references to these non-standard -classes. An alternate implementation of these (com.android.tools.layoutlib.java.*) is -injected. - -The ClassAdapters are chained together to achieve the desired output. (Look at section -2.2.7 Transformation chains in the asm user guide, link in the References.) The order of -execution of these is: +The next step of the transformation is changing the name of the class in case we requested the class +to be renamed. This uses the RenameClassAdapter to also rename all inner classes and references in +methods and types. Note that other classes are not transformed and keep referencing the original +name. + +The class is then fed to RefactorClassAdapter which is like RenameClassAdapter but updates the +references in all classes. This is used to update the references of classes in the java package that +were added in the Dalvik VM but are not a part of the standard JVM. The existing classes are +modified to update all references to these non-standard classes. An alternate implementation of +these (com.android.tools.layoutlib.java.*) is injected. + +RenameClassAdapter and RefactorClassAdapter both inherit from AbstractClassAdapter which changes the +class version (version of the JDK used to compile the class) to 50 (corresponding to Java 6), if the +class was originally compiled with Java 7 (version 51). This is because we don't currently generate +the StackMapTable correctly and Java 7 VM enforces that classes with version greater than 51 have +valid StackMapTable. As a side benefit of this, we can continue to support Java 6 because Java 7 on +Mac has horrible font rendering support. + +The ClassAdapters are chained together to achieve the desired output. (Look at section 2.2.7 +Transformation chains in the asm user guide, link in the References.) The order of execution of +these is: ClassReader -> [DelegateClassAdapter] -> TransformClassAdapter -> [RenameClassAdapter] -> RefactorClassAdapter -> ClassWriter - Method stubs -------------- -As indicated above, all native and overridden methods are replaced by a stub. -We don't have the code to replace with in layoutlib_create. -Instead the StubMethodAdapter replaces the code of the method by a call to -OverrideMethod.invokeX(). When using the final JAR, the bridge can register +As indicated above, all native and overridden methods are replaced by a stub. We don't have the +code to replace with in layoutlib_create. Instead the StubMethodAdapter replaces the code of the +method by a call to OverrideMethod.invokeX(). When using the final JAR, the bridge can register listeners from these overridden method calls based on the method signatures. -The listeners are currently pretty basic: we only pass the signature of the -method being called, its caller object and a flag indicating whether the -method was native. We do not currently provide the parameters. The listener -can however specify the return value of the overridden method. +The listeners are currently pretty basic: we only pass the signature of the method being called, its +caller object and a flag indicating whether the method was native. We do not currently provide the +parameters. The listener can however specify the return value of the overridden method. This strategy is now obsolete and replaced by the method delegates. @@ -156,97 +154,89 @@ This strategy is now obsolete and replaced by the method delegates. - Strategies ------------ -We currently have 6 strategies to deal with overriding the rendering code -and make it run in Eclipse. Most of these strategies are implemented hand-in-hand -by the bridge (which runs in Eclipse) and the generator. +We currently have 6 strategies to deal with overriding the rendering code and make it run in +Eclipse. Most of these strategies are implemented hand-in-hand by the bridge (which runs in Eclipse) +and the generator. 1- Class Injection This is the easiest: we currently inject the following classes: -- OverrideMethod and its associated MethodListener and MethodAdapter are used - to intercept calls to some specific methods that are stubbed out and change - their return value. -- CreateInfo class, which configured the generator. Not used yet, but could - in theory help us track what the generator changed. -- AutoCloseable and Objects are part of Java 7. To enable us to still run on Java 6, new - classes are injected. The implementation for these classes has been taken from - Android's libcore (platform/libcore/luni/src/main/java/java/...). -- Charsets, IntegralToString and UnsafeByteSequence are not part of the standard JAVA VM. - They are added to the Dalvik VM for performance reasons. An implementation that is very - close to the original (which is at platform/libcore/luni/src/main/java/...) is injected. - Since these classees were in part of the java package, where we can't inject classes, - all references to these have been updated (See strategy 4- Refactoring Classes). +- OverrideMethod and its associated MethodListener and MethodAdapter are used to intercept calls to + some specific methods that are stubbed out and change their return value. +- CreateInfo class, which configured the generator. Not used yet, but could in theory help us track + what the generator changed. +- AutoCloseable and Objects are part of Java 7. To enable us to still run on Java 6, new classes are + injected. The implementation for these classes has been taken from Android's libcore + (platform/libcore/luni/src/main/java/java/...). +- Charsets, IntegralToString and UnsafeByteSequence are not part of the standard JAVA VM. They are + added to the Dalvik VM for performance reasons. An implementation that is very close to the + original (which is at platform/libcore/luni/src/main/java/...) is injected. Since these classees + were in part of the java package, where we can't inject classes, all references to these have been + updated (See strategy 4- Refactoring Classes). 2- Overriding methods -As explained earlier, the creator doesn't have any replacement code for -methods to override. Instead it removes the original code and replaces it -by a call to a specific OveriddeMethod.invokeX(). The bridge then registers -a listener on the method signature and can provide an implementation. +As explained earlier, the creator doesn't have any replacement code for methods to override. Instead +it removes the original code and replaces it by a call to a specific OveriddeMethod.invokeX(). The +bridge then registers a listener on the method signature and can provide an implementation. -This strategy is now obsolete and replaced by the method delegates. -See strategy 5 below. +This strategy is now obsolete and replaced by the method delegates (See strategy 6- Method +Delegates). 3- Renaming classes -This simply changes the name of a class in its definition, as well as all its -references in internal inner classes and methods. -Calls from other classes are not modified -- they keep referencing the original -class name. This allows the bridge to literally replace an implementation. +This simply changes the name of a class in its definition, as well as all its references in internal +inner classes and methods. Calls from other classes are not modified -- they keep referencing the +original class name. This allows the bridge to literally replace an implementation. -An example will make this easier: android.graphics.Paint is the main drawing -class that we need to replace. To do so, the generator renames Paint to _original_Paint. -Later the bridge provides its own replacement version of Paint which will be used -by the rest of the Android stack. The replacement version of Paint can still use -(either by inheritance or delegation) all the original non-native code of _original_Paint -if it so desires. +An example will make this easier: android.graphics.Paint is the main drawing class that we need to +replace. To do so, the generator renames Paint to _original_Paint. Later the bridge provides its own +replacement version of Paint which will be used by the rest of the Android stack. The replacement +version of Paint can still use (either by inheritance or delegation) all the original non-native +code of _original_Paint if it so desires. -Some of the Android classes are basically wrappers over native objects and since -we don't have the native code in Eclipse, we need to provide a full alternate -implementation. Sub-classing doesn't work as some native methods are static and -we don't control object creation. +Some of the Android classes are basically wrappers over native objects and since we don't have the +native code in Eclipse, we need to provide a full alternate implementation. Sub-classing doesn't +work as some native methods are static and we don't control object creation. This won't rename/replace the inner static methods of a given class. 4- Refactoring classes -This is very similar to the Renaming classes except that it also updates the reference in -all classes. This is done for classes which are added to the Dalvik VM for performance -reasons but are not present in the Standard Java VM. An implementation for these classes -is also injected. +This is very similar to the Renaming classes except that it also updates the reference in all +classes. This is done for classes which are added to the Dalvik VM for performance reasons but are +not present in the Standard Java VM. An implementation for these classes is also injected. 5- Method erasure based on return type -This is mostly an implementation detail of the bridge: in the Paint class -mentioned above, some inner static classes are used to pass around -attributes (e.g. FontMetrics, or the Style enum) and all the original implementation -is native. +This is mostly an implementation detail of the bridge: in the Paint class mentioned above, some +inner static classes are used to pass around attributes (e.g. FontMetrics, or the Style enum) and +all the original implementation is native. -In this case we have a strategy that tells the generator that anything returning, for -example, the inner class Paint$Style in the Paint class should be discarded and the -bridge will provide its own implementation. +In this case we have a strategy that tells the generator that anything returning, for example, the +inner class Paint$Style in the Paint class should be discarded and the bridge will provide its own +implementation. 6- Method Delegates -This strategy is used to override method implementations. -Given a method SomeClass.MethodName(), 1 or 2 methods are generated: -a- A copy of the original method named SomeClass.MethodName_Original(). - The content is the original method as-is from the reader. - This step is omitted if the method is native, since it has no Java implementation. -b- A brand new implementation of SomeClass.MethodName() which calls to a - non-existing static method named SomeClass_Delegate.MethodName(). - The implementation of this 'delegate' method is done in layoutlib_brigde. - -The delegate method is a static method. -If the original method is non-static, the delegate method receives the original 'this' -as its first argument. If the original method is an inner non-static method, it also -receives the inner 'this' as the second argument. +This strategy is used to override method implementations. Given a method SomeClass.MethodName(), 1 +or 2 methods are generated: +a- A copy of the original method named SomeClass.MethodName_Original(). The content is the original +method as-is from the reader. This step is omitted if the method is native, since it has no Java +implementation. +b- A brand new implementation of SomeClass.MethodName() which calls to a non-existing static method +named SomeClass_Delegate.MethodName(). The implementation of this 'delegate' method is done in +layoutlib_brigde. + +The delegate method is a static method. If the original method is non-static, the delegate method +receives the original 'this' as its first argument. If the original method is an inner non-static +method, it also receives the inner 'this' as the second argument. diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AbstractClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AbstractClassAdapter.java index b2caa25..323a791 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AbstractClassAdapter.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AbstractClassAdapter.java @@ -177,6 +177,17 @@ public abstract class AbstractClassAdapter extends ClassVisitor { } } + /* Java 7 verifies the StackMapTable of a class if its version number is greater than 50.0. + * However, the check is disabled if the class version number is 50.0 or less. Generation + * of the StackMapTable requires a rewrite using the tree API of ASM. As a workaround, + * we rewrite the version number of the class to be 50.0 + * + * http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6693236 + */ + if (version > 50) { + version = 50; + } + super.visit(version, access, name, signature, superName, interfaces); } diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java index 911df7d..bb72a1e 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java @@ -125,6 +125,7 @@ public final class CreateInfo implements ICreateInfo { "android.app.Fragment#instantiate", //(Landroid/content/Context;Ljava/lang/String;Landroid/os/Bundle;)Landroid/app/Fragment;", "android.content.res.Resources$Theme#obtainStyledAttributes", "android.content.res.Resources$Theme#resolveAttribute", + "android.content.res.Resources#localeToLanguageTag", "android.content.res.AssetManager#newTheme", "android.content.res.AssetManager#deleteTheme", "android.content.res.AssetManager#applyThemeStyle", @@ -132,7 +133,6 @@ public final class CreateInfo implements ICreateInfo { "android.graphics.BitmapFactory#finishDecode", "android.os.Handler#sendMessageAtTime", "android.os.HandlerThread#run", - "android.os.Build#getString", "android.text.format.DateFormat#is24HourFormat", "android.view.Choreographer#getRefreshRate", "android.view.Display#updateDisplayInfoLocked", @@ -147,6 +147,7 @@ public final class CreateInfo implements ICreateInfo { "com.android.internal.view.menu.MenuBuilder#createNewMenuItem", "com.android.internal.util.XmlUtils#convertValueToInt", "com.android.internal.textservice.ITextServicesManager$Stub#asInterface", + "android.os.SystemProperties#native_get", }; /** diff --git a/tools/layoutlib/rename_font/README b/tools/layoutlib/rename_font/README new file mode 100644 index 0000000..600b756 --- /dev/null +++ b/tools/layoutlib/rename_font/README @@ -0,0 +1,9 @@ +This tool is used to rename the PS name encoded inside the ttf font that we ship +with the SDK. There is bug in Java that returns incorrect results for +java.awt.Font#layoutGlyphVector() if two fonts with same name but differnt +versions are loaded. As a workaround, we rename all the fonts that we ship with +the SDK by appending the font version to its name. + + +The build_font.py copies all files from input_dir to output_dir while renaming +the font files (*.ttf) in the process. diff --git a/tools/layoutlib/rename_font/Roboto-Regular.ttf b/tools/layoutlib/rename_font/Roboto-Regular.ttf Binary files differnew file mode 100644 index 0000000..7469063 --- /dev/null +++ b/tools/layoutlib/rename_font/Roboto-Regular.ttf diff --git a/tools/layoutlib/rename_font/build_font.py b/tools/layoutlib/rename_font/build_font.py new file mode 100755 index 0000000..ea3dccc --- /dev/null +++ b/tools/layoutlib/rename_font/build_font.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python + +# Copyright (C) 2014 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. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# 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. + +""" +Rename the PS name of all fonts in the input directory and copy them to the +output directory. + +Usage: build_font.py /path/to/input_fonts/ /path/to/output_fonts/ + +""" + +import sys +# fontTools is available at platform/external/fonttools +from fontTools import ttx +import re +import os +from lxml import etree +import shutil +import glob + +def main(argv): + if len(argv) != 2: + print "Usage: build_font.py /path/to/input_fonts/ /path/to/out/dir/" + sys.exit(1) + if not os.path.isdir(argv[0]): + print argv[0] + "is not a valid directory" + sys.exit(1) + if not os.path.isdir(argv[1]): + print argv[1] + "is not a valid directory" + sys.exit(1) + cwd = os.getcwd() + os.chdir(argv[1]) + files = glob.glob('*') + for filename in files: + os.remove(filename) + os.chdir(cwd) + for filename in os.listdir(argv[0]): + if not os.path.splitext(filename)[1].lower() == ".ttf": + shutil.copy(os.path.join(argv[0], filename), argv[1]) + continue + print os.path.join(argv[0], filename) + old_ttf_path = os.path.join(argv[0], filename) + # run ttx to generate an xml file in the output folder which represents all + # its info + ttx_args = ["-d", argv[1], old_ttf_path] + ttx.main(ttx_args) + # the path to the output file. The file name is the fontfilename.ttx + ttx_path = os.path.join(argv[1], filename) + ttx_path = ttx_path[:-1] + "x" + # now parse the xml file to change its PS name. + tree = etree.parse(ttx_path) + encoding = tree.docinfo.encoding + root = tree.getroot() + for name in root.iter('name'): + [old_ps_name, version] = get_font_info(name) + new_ps_name = old_ps_name + version + update_name(name, new_ps_name) + tree.write(ttx_path, xml_declaration=True, encoding=encoding ) + # generate the udpated font now. + ttx_args = ["-d", argv[1], ttx_path] + ttx.main(ttx_args) + # delete the temp ttx file. + os.remove(ttx_path) + +def get_font_info(tag): + ps_name = None + ps_version = None + for namerecord in tag.iter('namerecord'): + if 'nameID' in namerecord.attrib: + # if the tag has nameID=6, it is the postscript name of the font. + # see: http://scripts.sil.org/cms/scripts/page.php?item_id=IWS-Chapter08#3054f18b + if namerecord.attrib['nameID'] == '6': + if ps_name is not None: + if not sanitize(namerecord.text) == ps_name: + sys.exit('found multiple possibilities of the font name') + else: + ps_name = sanitize(namerecord.text) + # nameID=5 means the font version + if namerecord.attrib['nameID'] == '5': + if ps_version is not None: + if not ps_version == get_version(namerecord.text): + sys.exit('found multiple possibilities of the font version') + else: + ps_version = get_version(namerecord.text) + if ps_name is not None and ps_version is not None: + return [ps_name, ps_version] + sys.exit('didn\'t find the font name or version') + + +def update_name(tag, name): + for namerecord in tag.iter('namerecord'): + if 'nameID' in namerecord.attrib: + if namerecord.attrib['nameID'] == '6': + namerecord.text = name + +def sanitize(string): + return re.sub(r'[^\w-]+', '', string) + +def get_version(string): + # The string must begin with "Version n.nn " + # to extract n.nn, we return the second entry in the split strings. + string = string.strip() + if not string.startswith("Version "): + sys.exit('mal-formed font version') + return sanitize(string.split()[1]) + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/tools/layoutlib/rename_font/test.py b/tools/layoutlib/rename_font/test.py new file mode 100755 index 0000000..d4c86cb --- /dev/null +++ b/tools/layoutlib/rename_font/test.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python + +"""Tests build_font.py by renaming a font. + +The test copies Roboto-Regular.ttf to a tmp directory and ask build_font.py to rename it and put in another dir. +We then use ttx to dump the new font to its xml and check if rename was successful + +To test locally, use: +PYTHONPATH="$PYTHONPATH:/path/to/android/checkout/external/fonttools/Lib" ./test.py +""" + +import unittest +import build_font + +from fontTools import ttx +import os +from lxml import etree +import shutil +import tempfile + +class MyTest(unittest.TestCase): + def test(self): + font_name = "Roboto-Regular.ttf" + srcdir = tempfile.mkdtemp() + print "srcdir: " + srcdir + shutil.copy(font_name, srcdir) + destdir = tempfile.mkdtemp() + print "destdir: " + destdir + self.assertTrue(build_font.main([srcdir, destdir]) is None) + out_path = os.path.join(destdir, font_name) + ttx.main([out_path]) + ttx_path = out_path[:-1] + "x" + tree = etree.parse(ttx_path) + root = tree.getroot() + name_tag = root.find('name') + [f_name, f_version] = build_font.get_font_info(name_tag) + shutil.rmtree(srcdir) + shutil.rmtree(destdir) + self.assertEqual(f_name, "Roboto-Regular1200310") + + + +if __name__ == '__main__': + unittest.main() |