diff options
Diffstat (limited to 'tools')
27 files changed, 1042 insertions, 496 deletions
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp index 729a048..1f17316 100644 --- a/tools/aapt/AaptAssets.cpp +++ b/tools/aapt/AaptAssets.cpp @@ -149,205 +149,506 @@ static bool isHidden(const char *root, const char *path) // ========================================================================= // ========================================================================= -status_t -AaptGroupEntry::parseNamePart(const String8& part, int* axis, uint32_t* value) +/* 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(); + for (size_t i = 0; i < length; ++i) { + if (!isalpha(string[i])) { + return false; + } + } + + return true; +} + +/* static */ +inline bool isNumber(const String8& string) { + const size_t length = string.length(); + for (size_t i = 0; i < length; ++i) { + if (!isdigit(string[i])) { + return false; + } + } + + return true; +} + +void AaptLocaleValue::setLanguage(const char* languageChars) { + size_t i = 0; + while ((*languageChars) != '\0') { + language[i++] = tolower(*languageChars); + languageChars++; + } +} + +void AaptLocaleValue::setRegion(const char* regionChars) { + size_t i = 0; + while ((*regionChars) != '\0') { + region[i++] = toupper(*regionChars); + regionChars++; + } +} + +void AaptLocaleValue::setScript(const char* scriptChars) { + size_t i = 0; + while ((*scriptChars) != '\0') { + if (i == 0) { + script[i++] = toupper(*scriptChars); + } else { + script[i++] = tolower(*scriptChars); + } + scriptChars++; + } +} + +void AaptLocaleValue::setVariant(const char* variantChars) { + size_t i = 0; + while ((*variantChars) != '\0') { + variant[i++] = *variantChars; + 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, '_'); + + const int numTags = parts.size(); + bool valid = false; + if (numTags >= 1) { + const String8& lang = parts[0]; + if (isAlpha(lang) && (lang.length() == 2 || lang.length() == 3)) { + setLanguage(lang.string()); + valid = true; + } + } + + if (!valid || numTags == 1) { + return valid; + } + + // At this point, valid == true && numTags > 1. + const String8& part2 = parts[1]; + if ((part2.length() == 2 && isAlpha(part2)) || + (part2.length() == 3 && isNumber(part2))) { + setRegion(part2.string()); + } else if (part2.length() == 4 && isAlpha(part2)) { + setScript(part2.string()); + } else if (part2.length() >= 5 && part2.length() <= 8) { + setVariant(part2.string()); + } else { + valid = false; + } + + if (!valid || numTags == 2) { + return valid; + } + + // At this point, valid == true && numTags > 1. + const String8& part3 = parts[2]; + if (((part3.length() == 2 && isAlpha(part3)) || + (part3.length() == 3 && isNumber(part3))) && script[0]) { + setRegion(part3.string()); + } else if (part3.length() >= 5 && part3.length() <= 8) { + setVariant(part3.string()); + } else { + valid = false; + } + + if (!valid || numTags == 3) { + return valid; + } + + const String8& part4 = parts[3]; + if (part4.length() >= 5 && part4.length() <= 8) { + setVariant(part4.string()); + } else { + valid = false; + } + + if (!valid || numTags > 4) { + return false; + } + + return true; +} + +int AaptLocaleValue::initFromDirName(const Vector<String8>& parts, const int startIndex) { + const int size = parts.size(); + int currentIndex = startIndex; + + String8 part = parts[currentIndex]; + 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, '+'); + subtags.removeItemsAt(0); + if (subtags.size() == 1) { + setLanguage(subtags[0]); + } else if (subtags.size() == 2) { + setLanguage(subtags[0]); + + // The second tag can either be a region, a variant or a script. + switch (subtags[1].size()) { + case 2: + case 3: + setRegion(subtags[1]); + break; + case 4: + setScript(subtags[1]); + break; + case 5: + case 6: + case 7: + case 8: + setVariant(subtags[1]); + break; + default: + fprintf(stderr, "ERROR: Invalid BCP-47 tag in directory name %s\n", + part.string()); + return -1; + } + } else if (subtags.size() == 3) { + // The language is always the first subtag. + setLanguage(subtags[0]); + + // The second subtag can either be a script or a region code. + // If its size is 4, it's a script code, else it's a region code. + bool hasRegion = false; + if (subtags[1].size() == 4) { + setScript(subtags[1]); + } else if (subtags[1].size() == 2 || subtags[1].size() == 3) { + setRegion(subtags[1]); + hasRegion = true; + } else { + fprintf(stderr, "ERROR: Invalid BCP-47 tag in directory name %s\n", part.string()); + return -1; + } + + // The third tag can either be a region code (if the second tag was + // a script), else a variant code. + if (subtags[2].size() > 4) { + setVariant(subtags[2]); + } else { + setRegion(subtags[2]); + } + } else if (subtags.size() == 4) { + setLanguage(subtags[0]); + setScript(subtags[1]); + setRegion(subtags[2]); + setVariant(subtags[3]); + } else { + fprintf(stderr, "ERROR: Invalid BCP-47 tag in directory name: %s\n", part.string()); + return -1; + } + + return ++currentIndex; + } else { + if ((part.length() == 2 || part.length() == 3) && isAlpha(part)) { + setLanguage(part); + if (++currentIndex == size) { + return size; + } + } else { + return currentIndex; + } + + part = parts[currentIndex]; + if (part.string()[0] == 'r' && part.length() == 3) { + setRegion(part.string() + 1); + if (++currentIndex == size) { + return size; + } + } + } + + return currentIndex; +} + + +String8 AaptLocaleValue::toDirName() const { + String8 dirName(""); + if (language[0]) { + dirName += language; + } else { + return dirName; + } + + if (script[0]) { + dirName += "-s"; + dirName += script; + } + + if (region[0]) { + dirName += "-r"; + dirName += region; + } + + if (variant[0]) { + dirName += "-v"; + dirName += variant; + } + + return dirName; +} + +void AaptLocaleValue::initFromResTable(const ResTable_config& config) { + config.unpackLanguage(language); + config.unpackRegion(region); + if (config.localeScript[0]) { + memcpy(script, config.localeScript, sizeof(config.localeScript)); + } + + if (config.localeVariant[0]) { + memcpy(variant, config.localeVariant, sizeof(config.localeVariant)); + } +} + +void AaptLocaleValue::writeTo(ResTable_config* out) const { + out->packLanguage(language); + out->packRegion(region); + + if (script[0]) { + memcpy(out->localeScript, script, sizeof(out->localeScript)); + } + + if (variant[0]) { + memcpy(out->localeVariant, variant, sizeof(out->localeVariant)); + } +} + + +/* 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 = config.mcc; - return 0; + value->intValue = config.mcc; + return true; } // IMSI - MNC if (getMncName(part.string(), &config)) { *axis = AXIS_MNC; - *value = config.mnc; - return 0; + value->intValue = config.mnc; + return true; } // locale - language - if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) { - *axis = AXIS_LANGUAGE; - *value = part[1] << 8 | part[0]; - return 0; - } - - // locale - language_REGION - if (part.length() == 5 && isalpha(part[0]) && isalpha(part[1]) - && part[2] == '_' && isalpha(part[3]) && isalpha(part[4])) { - *axis = AXIS_LANGUAGE; - *value = (part[4] << 24) | (part[3] << 16) | (part[1] << 8) | (part[0]); - return 0; + if (value->localeValue.initFromFilterString(part)) { + *axis = AXIS_LOCALE; + return true; } // layout direction if (getLayoutDirectionName(part.string(), &config)) { *axis = AXIS_LAYOUTDIR; - *value = (config.screenLayout&ResTable_config::MASK_LAYOUTDIR); - return 0; + value->intValue = (config.screenLayout&ResTable_config::MASK_LAYOUTDIR); + return true; } // smallest screen dp width if (getSmallestScreenWidthDpName(part.string(), &config)) { *axis = AXIS_SMALLESTSCREENWIDTHDP; - *value = config.smallestScreenWidthDp; - return 0; + value->intValue = config.smallestScreenWidthDp; + return true; } // screen dp width if (getScreenWidthDpName(part.string(), &config)) { *axis = AXIS_SCREENWIDTHDP; - *value = config.screenWidthDp; - return 0; + value->intValue = config.screenWidthDp; + return true; } // screen dp height if (getScreenHeightDpName(part.string(), &config)) { *axis = AXIS_SCREENHEIGHTDP; - *value = config.screenHeightDp; - return 0; + value->intValue = config.screenHeightDp; + return true; } // screen layout size if (getScreenLayoutSizeName(part.string(), &config)) { *axis = AXIS_SCREENLAYOUTSIZE; - *value = (config.screenLayout&ResTable_config::MASK_SCREENSIZE); - return 0; + value->intValue = (config.screenLayout&ResTable_config::MASK_SCREENSIZE); + return true; } // screen layout long if (getScreenLayoutLongName(part.string(), &config)) { *axis = AXIS_SCREENLAYOUTLONG; - *value = (config.screenLayout&ResTable_config::MASK_SCREENLONG); - return 0; + value->intValue = (config.screenLayout&ResTable_config::MASK_SCREENLONG); + return true; } // orientation if (getOrientationName(part.string(), &config)) { *axis = AXIS_ORIENTATION; - *value = config.orientation; - return 0; + value->intValue = config.orientation; + return true; } // ui mode type if (getUiModeTypeName(part.string(), &config)) { *axis = AXIS_UIMODETYPE; - *value = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE); - return 0; + value->intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE); + return true; } // ui mode night if (getUiModeNightName(part.string(), &config)) { *axis = AXIS_UIMODENIGHT; - *value = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT); - return 0; + value->intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT); + return true; } // density if (getDensityName(part.string(), &config)) { *axis = AXIS_DENSITY; - *value = config.density; - return 0; + value->intValue = config.density; + return true; } // touchscreen if (getTouchscreenName(part.string(), &config)) { *axis = AXIS_TOUCHSCREEN; - *value = config.touchscreen; - return 0; + value->intValue = config.touchscreen; + return true; } // keyboard hidden if (getKeysHiddenName(part.string(), &config)) { *axis = AXIS_KEYSHIDDEN; - *value = config.inputFlags; - return 0; + value->intValue = config.inputFlags; + return true; } // keyboard if (getKeyboardName(part.string(), &config)) { *axis = AXIS_KEYBOARD; - *value = config.keyboard; - return 0; + value->intValue = config.keyboard; + return true; } // navigation hidden if (getNavHiddenName(part.string(), &config)) { *axis = AXIS_NAVHIDDEN; - *value = config.inputFlags; + value->intValue = config.inputFlags; return 0; } // navigation if (getNavigationName(part.string(), &config)) { *axis = AXIS_NAVIGATION; - *value = config.navigation; - return 0; + value->intValue = config.navigation; + return true; } // screen size if (getScreenSizeName(part.string(), &config)) { *axis = AXIS_SCREENSIZE; - *value = config.screenSize; - return 0; + value->intValue = config.screenSize; + return true; } // version if (getVersionName(part.string(), &config)) { *axis = AXIS_VERSION; - *value = config.version; - return 0; + value->intValue = config.version; + return true; } - return 1; + return false; } -uint32_t +AxisValue AaptGroupEntry::getConfigValueForAxis(const ResTable_config& config, int axis) { + AxisValue value; switch (axis) { case AXIS_MCC: - return config.mcc; + value.intValue = config.mcc; + break; case AXIS_MNC: - return config.mnc; - case AXIS_LANGUAGE: - return (((uint32_t)config.country[1]) << 24) | (((uint32_t)config.country[0]) << 16) - | (((uint32_t)config.language[1]) << 8) | (config.language[0]); + value.intValue = config.mnc; + break; + case AXIS_LOCALE: + value.localeValue.initFromResTable(config); + break; case AXIS_LAYOUTDIR: - return config.screenLayout&ResTable_config::MASK_LAYOUTDIR; + value.intValue = config.screenLayout&ResTable_config::MASK_LAYOUTDIR; + break; case AXIS_SCREENLAYOUTSIZE: - return config.screenLayout&ResTable_config::MASK_SCREENSIZE; + value.intValue = config.screenLayout&ResTable_config::MASK_SCREENSIZE; + break; case AXIS_ORIENTATION: - return config.orientation; + value.intValue = config.orientation; + break; case AXIS_UIMODETYPE: - return (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE); + value.intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE); + break; case AXIS_UIMODENIGHT: - return (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT); + value.intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT); + break; case AXIS_DENSITY: - return config.density; + value.intValue = config.density; + break; case AXIS_TOUCHSCREEN: - return config.touchscreen; + value.intValue = config.touchscreen; + break; case AXIS_KEYSHIDDEN: - return config.inputFlags; + value.intValue = config.inputFlags; + break; case AXIS_KEYBOARD: - return config.keyboard; + value.intValue = config.keyboard; + break; case AXIS_NAVIGATION: - return config.navigation; + value.intValue = config.navigation; + break; case AXIS_SCREENSIZE: - return config.screenSize; + value.intValue = config.screenSize; + break; case AXIS_SMALLESTSCREENWIDTHDP: - return config.smallestScreenWidthDp; + value.intValue = config.smallestScreenWidthDp; + break; case AXIS_SCREENWIDTHDP: - return config.screenWidthDp; + value.intValue = config.screenWidthDp; + break; case AXIS_SCREENHEIGHTDP: - return config.screenHeightDp; + value.intValue = config.screenHeightDp; + break; case AXIS_VERSION: - return config.version; + value.intValue = config.version; + break; } - return 0; + + return value; } bool @@ -371,24 +672,14 @@ AaptGroupEntry::initFromDirName(const char* dir, String8* resType) mParamsChanged = true; Vector<String8> parts; + AaptLocaleValue::splitAndLowerCase(dir, &parts, '-'); - String8 mcc, mnc, loc, layoutsize, layoutlong, orient, den; + String8 mcc, mnc, layoutsize, layoutlong, orient, den; String8 touch, key, keysHidden, nav, navHidden, size, layoutDir, vers; String8 uiModeType, uiModeNight, smallestwidthdp, widthdp, heightdp; - const char *p = dir; - const char *q; - while (NULL != (q = strchr(p, '-'))) { - String8 val(p, q-p); - val.toLower(); - parts.add(val); - //printf("part: %s\n", parts[parts.size()-1].string()); - p = q+1; - } - String8 val(p); - val.toLower(); - parts.add(val); - //printf("part: %s\n", parts[parts.size()-1].string()); + AaptLocaleValue locale; + int numLocaleComponents = 0; const int N = parts.size(); int index = 0; @@ -429,38 +720,18 @@ AaptGroupEntry::initFromDirName(const char* dir, String8* resType) } part = parts[index]; } else { - //printf("not mcc: %s\n", part.string()); + //printf("not mnc: %s\n", part.string()); } - // locale - language - if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) { - loc = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not language: %s\n", part.string()); + index = locale.initFromDirName(parts, index); + if (index == -1) { + return false; } - - // locale - region - if (loc.length() > 0 - && part.length() == 3 && part[0] == 'r' && part[0] && part[1]) { - loc += "-"; - part.toUpper(); - loc += part.string() + 1; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not region: %s\n", part.string()); + if (index >= N){ + goto success; } + part = parts[index]; if (getLayoutDirectionName(part.string())) { layoutDir = part; @@ -679,7 +950,7 @@ AaptGroupEntry::initFromDirName(const char* dir, String8* resType) success: this->mcc = mcc; this->mnc = mnc; - this->locale = loc; + this->locale = locale; this->screenLayoutSize = layoutsize; this->screenLayoutLong = layoutlong; this->smallestScreenWidthDp = smallestwidthdp; @@ -711,7 +982,7 @@ AaptGroupEntry::toString() const s += ","; s += this->mnc; s += ","; - s += this->locale; + s += locale.toDirName(); s += ","; s += layoutDirection; s += ","; @@ -765,12 +1036,15 @@ AaptGroupEntry::toDirName(const String8& resType) const } s += mnc; } - if (this->locale != "") { - if (s.length() > 0) { - s += "-"; - } - s += locale; + + const String8 localeComponent = locale.toDirName(); + if (localeComponent != "") { + if (s.length() > 0) { + s += "-"; + } + s += localeComponent; } + if (this->layoutDirection != "") { if (s.length() > 0) { s += "-"; @@ -942,55 +1216,6 @@ bool AaptGroupEntry::getMncName(const char* name, return true; } -/* - * Does this directory name fit the pattern of a locale dir ("en-rUS" or - * "default")? - * - * TODO: Should insist that the first two letters are lower case, and the - * second two are upper. - */ -bool AaptGroupEntry::getLocaleName(const char* fileName, - ResTable_config* out) -{ - if (strcmp(fileName, kWildcardName) == 0 - || strcmp(fileName, kDefaultLocale) == 0) { - if (out) { - out->language[0] = 0; - out->language[1] = 0; - out->country[0] = 0; - out->country[1] = 0; - } - return true; - } - - if (strlen(fileName) == 2 && isalpha(fileName[0]) && isalpha(fileName[1])) { - if (out) { - out->language[0] = fileName[0]; - out->language[1] = fileName[1]; - out->country[0] = 0; - out->country[1] = 0; - } - return true; - } - - if (strlen(fileName) == 5 && - isalpha(fileName[0]) && - isalpha(fileName[1]) && - fileName[2] == '-' && - isalpha(fileName[3]) && - isalpha(fileName[4])) { - if (out) { - out->language[0] = fileName[0]; - out->language[1] = fileName[1]; - out->country[0] = fileName[3]; - out->country[1] = fileName[4]; - } - return true; - } - - return false; -} - bool AaptGroupEntry::getLayoutDirectionName(const char* name, ResTable_config* out) { if (strcmp(name, kWildcardName) == 0) { @@ -1501,18 +1726,18 @@ int AaptGroupEntry::compare(const AaptGroupEntry& o) const return v; } -const ResTable_config& AaptGroupEntry::toParams() const +const ResTable_config AaptGroupEntry::toParams() const { if (!mParamsChanged) { return mParams; } mParamsChanged = false; - ResTable_config& params(mParams); - memset(¶ms, 0, sizeof(params)); + ResTable_config& params = mParams; + memset(¶ms, 0, sizeof(ResTable_config)); getMccName(mcc.string(), ¶ms); getMncName(mnc.string(), ¶ms); - getLocaleName(locale.string(), ¶ms); + locale.writeTo(¶ms); getLayoutDirectionName(layoutDirection.string(), ¶ms); getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), ¶ms); getScreenWidthDpName(screenWidthDp.string(), ¶ms); @@ -1997,7 +2222,9 @@ status_t AaptSymbols::applyJavaSymbols(const sp<AaptSymbols>& javaSymbols) AaptAssets::AaptAssets() : AaptDir(String8(), String8()), - mChanged(false), mHaveIncludedAssets(false), mRes(NULL) + mHavePrivateSymbols(false), + mChanged(false), mHaveIncludedAssets(false), + mRes(NULL) { } @@ -2507,9 +2734,9 @@ status_t AaptAssets::filter(Bundle* bundle) // If our preferred density is hdpi but we only have mdpi and xhdpi resources, we // pick xhdpi. uint32_t preferredDensity = 0; - const SortedVector<uint32_t>* preferredConfigs = prefFilter.configsForAxis(AXIS_DENSITY); + const SortedVector<AxisValue>* preferredConfigs = prefFilter.configsForAxis(AXIS_DENSITY); if (preferredConfigs != NULL && preferredConfigs->size() > 0) { - preferredDensity = (*preferredConfigs)[0]; + preferredDensity = (*preferredConfigs)[0].intValue; } // Now deal with preferred configurations. @@ -2667,7 +2894,7 @@ status_t AaptAssets::addIncludedResources(const sp<AaptFile>& file) { const ResTable& res = getIncludedResources(); // XXX dirty! - return const_cast<ResTable&>(res).add(file->getData(), file->getSize(), NULL); + return const_cast<ResTable&>(res).add(file->getData(), file->getSize()); } const ResTable& AaptAssets::getIncludedResources() const diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h index 9cc9007..336d08b 100644 --- a/tools/aapt/AaptAssets.h +++ b/tools/aapt/AaptAssets.h @@ -13,7 +13,6 @@ #include <utils/RefBase.h> #include <utils/SortedVector.h> #include <utils/String8.h> -#include <utils/String8.h> #include <utils/Vector.h> #include "ZipFile.h" @@ -34,8 +33,7 @@ enum { AXIS_NONE = 0, AXIS_MCC = 1, AXIS_MNC, - AXIS_LANGUAGE, - AXIS_REGION, + AXIS_LOCALE, AXIS_SCREENLAYOUTSIZE, AXIS_SCREENLAYOUTLONG, AXIS_ORIENTATION, @@ -58,6 +56,73 @@ enum { AXIS_END = AXIS_VERSION, }; +struct AaptLocaleValue { + char language[4]; + char region[4]; + char script[4]; + char variant[8]; + + AaptLocaleValue() { + memset(this, 0, sizeof(AaptLocaleValue)); + } + + // Initialize this AaptLocaleValue from a config string. + bool initFromFilterString(const String8& config); + + int initFromDirName(const Vector<String8>& parts, const int startIndex); + + // Initialize this AaptLocaleValue from a ResTable_config. + void initFromResTable(const ResTable_config& config); + + void writeTo(ResTable_config* out) const; + + String8 toDirName() const; + + int compare(const AaptLocaleValue& other) const { + 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; } + 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; } +private: + void setLanguage(const char* language); + void setRegion(const char* language); + void setScript(const char* script); + 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. @@ -65,22 +130,38 @@ enum { struct AaptGroupEntry { public: - AaptGroupEntry() : mParamsChanged(true) { } - AaptGroupEntry(const String8& _locale, const String8& _vendor) - : locale(_locale), vendor(_vendor), mParamsChanged(true) { } + AaptGroupEntry() : mParamsChanged(true) { + memset(&mParams, 0, sizeof(ResTable_config)); + } bool initFromDirName(const char* dir, String8* resType); - static status_t parseNamePart(const String8& part, int* axis, uint32_t* value); + static bool parseFilterNamePart(const String8& part, int* axis, AxisValue* value); - static uint32_t getConfigValueForAxis(const ResTable_config& config, int axis); + 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 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; } + 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; } + + String8 toString() const; + String8 toDirName(const String8& resType) const; + + const String8& getVersionString() const { return version; } + +private: static bool getMccName(const char* name, ResTable_config* out = NULL); static bool getMncName(const char* name, ResTable_config* out = NULL); - static bool getLocaleName(const char* name, ResTable_config* out = NULL); 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); @@ -99,26 +180,9 @@ public: static bool getLayoutDirectionName(const char* name, ResTable_config* out = NULL); static bool getVersionName(const char* name, ResTable_config* out = NULL); - int compare(const AaptGroupEntry& o) const; - - const ResTable_config& toParams() const; - - inline bool operator<(const AaptGroupEntry& o) const { return compare(o) < 0; } - inline bool operator<=(const AaptGroupEntry& o) const { return compare(o) <= 0; } - 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; } - inline bool operator>(const AaptGroupEntry& o) const { return compare(o) > 0; } - - String8 toString() const; - String8 toDirName(const String8& resType) const; - - const String8& getVersionString() const { return version; } - -private: String8 mcc; String8 mnc; - String8 locale; + AaptLocaleValue locale; String8 vendor; String8 smallestScreenWidthDp; String8 screenWidthDp; diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h index 26b10a6..382cf5e 100644 --- a/tools/aapt/Bundle.h +++ b/tools/aapt/Bundle.h @@ -42,6 +42,15 @@ typedef enum Command { } Command; /* + * Pseudolocalization methods + */ +typedef enum PseudolocalizationMethod { + NO_PSEUDOLOCALIZATION = 0, + PSEUDO_ACCENTED, + PSEUDO_BIDI, +} PseudolocalizationMethod; + +/* * Bundle of goodies, including everything specified on the command line. */ class Bundle { @@ -50,7 +59,7 @@ public: : mCmd(kCommandUnknown), mVerbose(false), mAndroidList(false), mForce(false), mGrayscaleTolerance(0), mMakePackageDirs(false), mUpdate(false), mExtending(false), - mRequireLocalization(false), mPseudolocalize(false), + mRequireLocalization(false), mPseudolocalize(NO_PSEUDOLOCALIZATION), mWantUTF16(false), mValues(false), mCompressionMethod(0), mJunkPath(false), mOutputAPKFile(NULL), mManifestPackageNameOverride(NULL), mInstrumentationPackageNameOverride(NULL), @@ -93,8 +102,8 @@ public: void setExtending(bool val) { mExtending = val; } bool getRequireLocalization(void) const { return mRequireLocalization; } void setRequireLocalization(bool val) { mRequireLocalization = val; } - bool getPseudolocalize(void) const { return mPseudolocalize; } - void setPseudolocalize(bool val) { mPseudolocalize = val; } + short getPseudolocalize(void) const { return mPseudolocalize; } + void setPseudolocalize(short val) { mPseudolocalize = val; } void setWantUTF16(bool val) { mWantUTF16 = val; } bool getValues(void) const { return mValues; } void setValues(bool val) { mValues = val; } @@ -249,7 +258,7 @@ private: bool mUpdate; bool mExtending; bool mRequireLocalization; - bool mPseudolocalize; + short mPseudolocalize; bool mWantUTF16; bool mValues; int mCompressionMethod; diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index d9e2dc5..34292fe 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -518,6 +518,7 @@ int doDump(Bundle* bundle) // the API version because key resources like icons will have an implicit // version if they are using newer config types like density. ResTable_config config; + memset(&config, 0, sizeof(ResTable_config)); config.language[0] = 'e'; config.language[1] = 'n'; config.country[0] = 'U'; @@ -1908,14 +1909,17 @@ int doPackage(Bundle* bundle) FILE* fp; String8 dependencyFile; - // -c zz_ZZ means do pseudolocalization + // -c en_XA or/and ar_XB means do pseudolocalization ResourceFilter filter; err = filter.parse(bundle->getConfigurations()); if (err != NO_ERROR) { goto bail; } if (filter.containsPseudo()) { - bundle->setPseudolocalize(true); + bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED); + } + if (filter.containsPseudoBidi()) { + bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI); } N = bundle->getFileSpecCount(); diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp index 25a948d..db74831 100644 --- a/tools/aapt/Images.cpp +++ b/tools/aapt/Images.cpp @@ -35,7 +35,9 @@ png_flush_aapt_file(png_structp png_ptr) // This holds an image as 8bpp RGBA. struct image_info { - image_info() : rows(NULL), is9Patch(false), allocRows(NULL) { } + image_info() : rows(NULL), is9Patch(false), + xDivs(NULL), yDivs(NULL), colors(NULL), allocRows(NULL) { } + ~image_info() { if (rows && rows != allocRows) { free(rows); @@ -46,9 +48,15 @@ struct image_info } free(allocRows); } - free(info9Patch.xDivs); - free(info9Patch.yDivs); - free(info9Patch.colors); + free(xDivs); + free(yDivs); + free(colors); + } + + void* serialize9patch() { + void* serialized = Res_png_9patch::serialize(info9Patch, xDivs, yDivs, colors); + reinterpret_cast<Res_png_9patch*>(serialized)->deviceToFile(); + return serialized; } png_uint_32 width; @@ -58,6 +66,9 @@ struct image_info // 9-patch info. bool is9Patch; Res_png_9patch info9Patch; + int32_t* xDivs; + int32_t* yDivs; + uint32_t* colors; // Layout padding, if relevant bool haveLayoutBounds; @@ -430,10 +441,10 @@ static uint32_t get_color(image_info* image, int hpatch, int vpatch) { int left, right, top, bottom; select_patch( - hpatch, image->info9Patch.xDivs[0], image->info9Patch.xDivs[1], + hpatch, image->xDivs[0], image->xDivs[1], image->width, &left, &right); select_patch( - vpatch, image->info9Patch.yDivs[0], image->info9Patch.yDivs[1], + vpatch, image->yDivs[0], image->yDivs[1], image->height, &top, &bottom); //printf("Selecting h=%d v=%d: (%d,%d)-(%d,%d)\n", // hpatch, vpatch, left, top, right, bottom); @@ -452,8 +463,8 @@ static status_t do_9patch(const char* imageName, image_info* image) int maxSizeXDivs = W * sizeof(int32_t); int maxSizeYDivs = H * sizeof(int32_t); - int32_t* xDivs = image->info9Patch.xDivs = (int32_t*) malloc(maxSizeXDivs); - int32_t* yDivs = image->info9Patch.yDivs = (int32_t*) malloc(maxSizeYDivs); + int32_t* xDivs = image->xDivs = (int32_t*) malloc(maxSizeXDivs); + int32_t* yDivs = image->yDivs = (int32_t*) malloc(maxSizeYDivs); uint8_t numXDivs = 0; uint8_t numYDivs = 0; @@ -609,7 +620,7 @@ static status_t do_9patch(const char* imageName, image_info* image) numColors = numRows * numCols; image->info9Patch.numColors = numColors; - image->info9Patch.colors = (uint32_t*)malloc(numColors * sizeof(uint32_t)); + image->colors = (uint32_t*)malloc(numColors * sizeof(uint32_t)); // Fill in color information for each patch. @@ -652,7 +663,7 @@ static status_t do_9patch(const char* imageName, image_info* image) right = xDivs[i]; } c = get_color(image->rows, left, top, right - 1, bottom - 1); - image->info9Patch.colors[colorIndex++] = c; + image->colors[colorIndex++] = c; NOISY(if (c != Res_png_9patch::NO_COLOR) hasColor = true); left = right; } @@ -664,14 +675,10 @@ static status_t do_9patch(const char* imageName, image_info* image) for (i=0; i<numColors; i++) { if (hasColor) { if (i == 0) printf("Colors in %s:\n ", imageName); - printf(" #%08x", image->info9Patch.colors[i]); + printf(" #%08x", image->colors[i]); if (i == numColors - 1) printf("\n"); } } - - image->is9Patch = true; - image->info9Patch.deviceToFile(); - getout: if (errorMsg) { fprintf(stderr, @@ -691,14 +698,10 @@ getout: return NO_ERROR; } -static void checkNinePatchSerialization(Res_png_9patch* inPatch, void * data) +static void checkNinePatchSerialization(Res_png_9patch* inPatch, void* data) { - if (sizeof(void*) != sizeof(int32_t)) { - // can't deserialize on a non-32 bit system - return; - } size_t patchSize = inPatch->serializedSize(); - void * newData = malloc(patchSize); + void* newData = malloc(patchSize); memcpy(newData, data, patchSize); Res_png_9patch* outPatch = inPatch->deserialize(newData); // deserialization is done in place, so outPatch == newData @@ -721,34 +724,6 @@ static void checkNinePatchSerialization(Res_png_9patch* inPatch, void * data) free(newData); } -static bool patch_equals(Res_png_9patch& patch1, Res_png_9patch& patch2) { - if (!(patch1.numXDivs == patch2.numXDivs && - patch1.numYDivs == patch2.numYDivs && - patch1.numColors == patch2.numColors && - patch1.paddingLeft == patch2.paddingLeft && - patch1.paddingRight == patch2.paddingRight && - patch1.paddingTop == patch2.paddingTop && - patch1.paddingBottom == patch2.paddingBottom)) { - return false; - } - for (int i = 0; i < patch1.numColors; i++) { - if (patch1.colors[i] != patch2.colors[i]) { - return false; - } - } - for (int i = 0; i < patch1.numXDivs; i++) { - if (patch1.xDivs[i] != patch2.xDivs[i]) { - return false; - } - } - for (int i = 0; i < patch1.numYDivs; i++) { - if (patch1.yDivs[i] != patch2.yDivs[i]) { - return false; - } - } - return true; -} - static void dump_image(int w, int h, png_bytepp rows, int color_type) { int i, j, rr, gg, bb, aa; @@ -1061,7 +1036,7 @@ static void write_png(const char* imageName, : (png_byte*)"npTc"; NOISY(printf("Adding 9-patch info...\n")); strcpy((char*)unknowns[p_index].name, "npTc"); - unknowns[p_index].data = (png_byte*)imageInfo.info9Patch.serialize(); + unknowns[p_index].data = (png_byte*)imageInfo.serialize9patch(); unknowns[p_index].size = imageInfo.info9Patch.serializedSize(); // TODO: remove the check below when everything works checkNinePatchSerialization(&imageInfo.info9Patch, unknowns[p_index].data); diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index 5865b00..42b1905 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -80,6 +80,7 @@ public: ResourceDirIterator(const sp<ResourceTypeSet>& set, const String8& resType) : mResType(resType), mSet(set), mSetPos(0), mGroupPos(0) { + memset(&mParams, 0, sizeof(ResTable_config)); } inline const sp<AaptGroup>& getGroup() const { return mGroup; } @@ -1321,8 +1322,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) } // Read resources back in, - finalResTable.add(resFile->getData(), resFile->getSize(), NULL); - + finalResTable.add(resFile->getData(), resFile->getSize()); #if 0 NOISY( printf("Generated resources:\n"); diff --git a/tools/aapt/ResourceFilter.cpp b/tools/aapt/ResourceFilter.cpp index 8cfd2a5..8ca852e 100644 --- a/tools/aapt/ResourceFilter.cpp +++ b/tools/aapt/ResourceFilter.cpp @@ -24,12 +24,14 @@ ResourceFilter::parse(const char* arg) String8 part(p, q-p); - if (part == "zz_ZZ") { - mContainsPseudo = true; + if (part == "en_XA") { + mContainsPseudoAccented = true; + } else if (part == "ar_XB") { + mContainsPseudoBidi = true; } int axis; - uint32_t value; - if (AaptGroupEntry::parseNamePart(part, &axis, &value)) { + 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++) { @@ -44,15 +46,20 @@ ResourceFilter::parse(const char* arg) ssize_t index = mData.indexOfKey(axis); if (index < 0) { - mData.add(axis, SortedVector<uint32_t>()); + mData.add(axis, SortedVector<AxisValue>()); } - SortedVector<uint32_t>& sv = mData.editValueFor(axis); + SortedVector<AxisValue>& sv = mData.editValueFor(axis); sv.add(value); - // if it's a locale with a region, also match an unmodified locale of the - // same language - if (axis == AXIS_LANGUAGE) { - if (value & 0xffff0000) { - sv.add(value & 0x0000ffff); + + // 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); } } p = q; @@ -70,9 +77,9 @@ ResourceFilter::isEmpty() const } bool -ResourceFilter::match(int axis, uint32_t value) const +ResourceFilter::match(int axis, const AxisValue& value) const { - if (value == 0) { + if (value.intValue == 0 && (value.localeValue.language[0] == 0)) { // they didn't specify anything so take everything return true; } @@ -81,7 +88,7 @@ ResourceFilter::match(int axis, uint32_t value) const // we didn't request anything on this axis so take everything return true; } - const SortedVector<uint32_t>& sv = mData.valueAt(index); + const SortedVector<AxisValue>& sv = mData.valueAt(index); return sv.indexOf(value) >= 0; } @@ -102,7 +109,7 @@ ResourceFilter::match(const ResTable_config& config) const return true; } -const SortedVector<uint32_t>* ResourceFilter::configsForAxis(int axis) const +const SortedVector<AxisValue>* ResourceFilter::configsForAxis(int axis) const { ssize_t index = mData.indexOfKey(axis); if (index < 0) { diff --git a/tools/aapt/ResourceFilter.h b/tools/aapt/ResourceFilter.h index 647b7bb..c57770e 100644 --- a/tools/aapt/ResourceFilter.h +++ b/tools/aapt/ResourceFilter.h @@ -16,18 +16,22 @@ class ResourceFilter { public: - ResourceFilter() : mData(), mContainsPseudo(false) {} + ResourceFilter() : mData(), mContainsPseudoAccented(false), + mContainsPseudoBidi(false) {} status_t parse(const char* arg); bool isEmpty() const; - bool match(int axis, uint32_t value) const; bool match(int axis, const ResTable_config& config) const; bool match(const ResTable_config& config) const; - const SortedVector<uint32_t>* configsForAxis(int axis) const; - inline bool containsPseudo() const { return mContainsPseudo; } + const SortedVector<AxisValue>* configsForAxis(int axis) const; + inline bool containsPseudo() const { return mContainsPseudoAccented; } + inline bool containsPseudoBidi() const { return mContainsPseudoBidi; } private: - KeyedVector<int,SortedVector<uint32_t> > mData; - bool mContainsPseudo; + bool match(int axis, const AxisValue& value) const; + + KeyedVector<int,SortedVector<AxisValue> > mData; + bool mContainsPseudoAccented; + bool mContainsPseudoBidi; }; #endif diff --git a/tools/aapt/ResourceIdCache.h b/tools/aapt/ResourceIdCache.h index 65f7781..e6bcda2 100644 --- a/tools/aapt/ResourceIdCache.h +++ b/tools/aapt/ResourceIdCache.h @@ -7,7 +7,6 @@ #define RESOURCE_ID_CACHE_H namespace android { -class android::String16; class ResourceIdCache { public: diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index 6ced8b3..cf271a9 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -25,7 +25,7 @@ status_t compileXmlFile(const sp<AaptAssets>& assets, if (root == NULL) { return UNKNOWN_ERROR; } - + return compileXmlFile(assets, root, target, table, options); } @@ -577,13 +577,13 @@ status_t parseAndAddBag(Bundle* bundle, int32_t curFormat, bool isFormatted, const String16& product, - bool pseudolocalize, + PseudolocalizationMethod pseudolocalize, const bool overwrite, ResourceTable* outTable) { status_t err; const String16 item16("item"); - + String16 str; Vector<StringPool::entry_style_span> spans; err = parseStyledString(bundle, in->getPrintableSource().string(), @@ -672,7 +672,7 @@ status_t parseAndAddEntry(Bundle* bundle, int32_t curFormat, bool isFormatted, const String16& product, - bool pseudolocalize, + PseudolocalizationMethod pseudolocalize, const bool overwrite, KeyedVector<type_ident_pair_t, bool>* skippedResourceNames, ResourceTable* outTable) @@ -854,10 +854,31 @@ status_t compileResourceFile(Bundle* bundle, ResTable_config curParams(defParams); ResTable_config pseudoParams(curParams); - pseudoParams.language[0] = 'z'; - pseudoParams.language[1] = 'z'; - pseudoParams.country[0] = 'Z'; - pseudoParams.country[1] = 'Z'; + pseudoParams.language[0] = 'e'; + pseudoParams.language[1] = 'n'; + pseudoParams.country[0] = 'X'; + pseudoParams.country[1] = 'A'; + + ResTable_config pseudoBidiParams(curParams); + pseudoBidiParams.language[0] = 'a'; + pseudoBidiParams.language[1] = 'r'; + pseudoBidiParams.country[0] = 'X'; + pseudoBidiParams.country[1] = 'B'; + + // We should skip resources for pseudolocales if they were + // already added automatically. This is a fix for a transition period when + // manually pseudolocalized resources may be expected. + // TODO: remove this check after next SDK version release. + if ((bundle->getPseudolocalize() & PSEUDO_ACCENTED && + curParams.locale == pseudoParams.locale) || + (bundle->getPseudolocalize() & PSEUDO_BIDI && + curParams.locale == pseudoBidiParams.locale)) { + SourcePos(in->getPrintableSource(), 0).warning( + "Resource file %s is skipped as pseudolocalization" + " was done automatically.", + in->getPrintableSource().string()); + return NO_ERROR; + } while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { if (code == ResXMLTree::START_TAG) { @@ -1292,8 +1313,8 @@ status_t compileResourceFile(Bundle* bundle, curIsStyled = true; } else if (strcmp16(block.getElementName(&len), string16.string()) == 0) { // Note the existence and locale of every string we process - char rawLocale[16]; - curParams.getLocale(rawLocale); + char rawLocale[RESTABLE_MAX_LOCALE_LEN]; + curParams.getBcp47Locale(rawLocale); String8 locale(rawLocale); String16 name; String16 translatable; @@ -1317,9 +1338,9 @@ status_t compileResourceFile(Bundle* bundle, curIsFormatted = false; // Untranslatable strings must only exist in the default [empty] locale if (locale.size() > 0) { - fprintf(stderr, "aapt: warning: string '%s' in %s marked untranslatable but exists" - " in locale '%s'\n", String8(name).string(), - bundle->getResourceSourceDirs()[0], + SourcePos(in->getPrintableSource(), block.getLineNumber()).warning( + "string '%s' marked untranslatable but exists in locale '%s'\n", + String8(name).string(), locale.string()); // hasErrors = localHasErrors = true; } else { @@ -1330,7 +1351,10 @@ status_t compileResourceFile(Bundle* bundle, // having no default translation. } } else { - outTable->addLocalization(name, locale); + outTable->addLocalization( + name, + locale, + SourcePos(in->getPrintableSource(), block.getLineNumber())); } if (formatted == false16) { @@ -1342,7 +1366,7 @@ status_t compileResourceFile(Bundle* bundle, curType = string16; curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING; curIsStyled = true; - curIsPseudolocalizable = (translatable != false16); + curIsPseudolocalizable = fileIsTranslatable && (translatable != false16); } else if (strcmp16(block.getElementName(&len), drawable16.string()) == 0) { curTag = &drawable16; curType = drawable16; @@ -1386,6 +1410,7 @@ status_t compileResourceFile(Bundle* bundle, curTag = &plurals16; curType = plurals16; curIsBag = true; + curIsPseudolocalizable = fileIsTranslatable; } else if (strcmp16(block.getElementName(&len), array16.string()) == 0) { curTag = &array16; curType = array16; @@ -1407,25 +1432,23 @@ status_t compileResourceFile(Bundle* bundle, } else if (strcmp16(block.getElementName(&len), string_array16.string()) == 0) { // Check whether these strings need valid formats. // (simplified form of what string16 does above) + bool isTranslatable = false; size_t n = block.getAttributeCount(); // Pseudolocalizable by default, unless this string array isn't // translatable. - curIsPseudolocalizable = true; for (size_t i = 0; i < n; i++) { size_t length; const uint16_t* attr = block.getAttributeName(i, &length); - if (strcmp16(attr, translatable16.string()) == 0) { + if (strcmp16(attr, formatted16.string()) == 0) { const uint16_t* value = block.getAttributeStringValue(i, &length); if (strcmp16(value, false16.string()) == 0) { - curIsPseudolocalizable = false; + curIsFormatted = false; } - } - - if (strcmp16(attr, formatted16.string()) == 0) { + } else if (strcmp16(attr, translatable16.string()) == 0) { const uint16_t* value = block.getAttributeStringValue(i, &length); if (strcmp16(value, false16.string()) == 0) { - curIsFormatted = false; + isTranslatable = false; } } } @@ -1435,6 +1458,7 @@ status_t compileResourceFile(Bundle* bundle, curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING; curIsBag = true; curIsBagReplaceOnOverwrite = true; + curIsPseudolocalizable = isTranslatable && fileIsTranslatable; } else if (strcmp16(block.getElementName(&len), integer_array16.string()) == 0) { curTag = &integer_array16; curType = array16; @@ -1556,19 +1580,29 @@ status_t compileResourceFile(Bundle* bundle, err = parseAndAddBag(bundle, in, &block, curParams, myPackage, curType, ident, parentIdent, itemIdent, curFormat, curIsFormatted, - product, false, overwrite, outTable); + product, NO_PSEUDOLOCALIZATION, overwrite, outTable); if (err == NO_ERROR) { if (curIsPseudolocalizable && localeIsDefined(curParams) - && bundle->getPseudolocalize()) { + && bundle->getPseudolocalize() > 0) { // pseudolocalize here -#if 1 - block.setPosition(parserPosition); - err = parseAndAddBag(bundle, in, &block, pseudoParams, myPackage, - curType, ident, parentIdent, itemIdent, curFormat, - curIsFormatted, product, true, overwrite, outTable); -#endif + if ((PSEUDO_ACCENTED & bundle->getPseudolocalize()) == + PSEUDO_ACCENTED) { + block.setPosition(parserPosition); + err = parseAndAddBag(bundle, in, &block, pseudoParams, myPackage, + curType, ident, parentIdent, itemIdent, curFormat, + curIsFormatted, product, PSEUDO_ACCENTED, + overwrite, outTable); + } + if ((PSEUDO_BIDI & bundle->getPseudolocalize()) == + PSEUDO_BIDI) { + block.setPosition(parserPosition); + err = parseAndAddBag(bundle, in, &block, pseudoBidiParams, myPackage, + curType, ident, parentIdent, itemIdent, curFormat, + curIsFormatted, product, PSEUDO_BIDI, + overwrite, outTable); + } } - } + } if (err != NO_ERROR) { hasErrors = localHasErrors = true; } @@ -1589,20 +1623,31 @@ status_t compileResourceFile(Bundle* bundle, err = parseAndAddEntry(bundle, in, &block, curParams, myPackage, curType, ident, *curTag, curIsStyled, curFormat, curIsFormatted, - product, false, overwrite, &skippedResourceNames, outTable); + product, NO_PSEUDOLOCALIZATION, overwrite, &skippedResourceNames, outTable); if (err < NO_ERROR) { // Why err < NO_ERROR instead of err != NO_ERROR? hasErrors = localHasErrors = true; } else if (err == NO_ERROR) { if (curIsPseudolocalizable && localeIsDefined(curParams) - && bundle->getPseudolocalize()) { + && bundle->getPseudolocalize() > 0) { // pseudolocalize here - block.setPosition(parserPosition); - err = parseAndAddEntry(bundle, in, &block, pseudoParams, myPackage, curType, - ident, *curTag, curIsStyled, curFormat, - curIsFormatted, product, - true, overwrite, &skippedResourceNames, outTable); + if ((PSEUDO_ACCENTED & bundle->getPseudolocalize()) == + PSEUDO_ACCENTED) { + block.setPosition(parserPosition); + err = parseAndAddEntry(bundle, in, &block, pseudoParams, myPackage, curType, + ident, *curTag, curIsStyled, curFormat, + curIsFormatted, product, + PSEUDO_ACCENTED, overwrite, &skippedResourceNames, outTable); + } + if ((PSEUDO_BIDI & bundle->getPseudolocalize()) == + PSEUDO_BIDI) { + block.setPosition(parserPosition); + err = parseAndAddEntry(bundle, in, &block, pseudoBidiParams, + myPackage, curType, ident, *curTag, curIsStyled, curFormat, + curIsFormatted, product, + PSEUDO_BIDI, overwrite, &skippedResourceNames, outTable); + } if (err != NO_ERROR) { hasErrors = localHasErrors = true; } @@ -2570,9 +2615,9 @@ status_t ResourceTable::addSymbols(const sp<AaptSymbols>& outSymbols) { void -ResourceTable::addLocalization(const String16& name, const String8& locale) +ResourceTable::addLocalization(const String16& name, const String8& locale, const SourcePos& src) { - mLocalizations[name].insert(locale); + mLocalizations[name][locale] = src; } @@ -2592,21 +2637,22 @@ ResourceTable::validateLocalizations(void) const String8 defaultLocale; // For all strings... - for (map<String16, set<String8> >::iterator nameIter = mLocalizations.begin(); + for (map<String16, map<String8, SourcePos> >::iterator nameIter = mLocalizations.begin(); nameIter != mLocalizations.end(); nameIter++) { - const set<String8>& configSet = nameIter->second; // naming convenience + const map<String8, SourcePos>& configSrcMap = nameIter->second; // Look for strings with no default localization - if (configSet.count(defaultLocale) == 0) { - fprintf(stdout, "aapt: warning: string '%s' has no default translation in %s; found:", - String8(nameIter->first).string(), mBundle->getResourceSourceDirs()[0]); - for (set<String8>::const_iterator locales = configSet.begin(); - locales != configSet.end(); - locales++) { - fprintf(stdout, " %s", (*locales).string()); - } - fprintf(stdout, "\n"); + if (configSrcMap.count(defaultLocale) == 0) { + SourcePos().warning("string '%s' has no default translation.", + String8(nameIter->first).string()); + if (mBundle->getVerbose()) { + for (map<String8, SourcePos>::const_iterator locales = configSrcMap.begin(); + locales != configSrcMap.end(); + locales++) { + locales->second.printf("locale %s found", locales->first.string()); + } + } // !!! TODO: throw an error here in some circumstances } @@ -2616,6 +2662,8 @@ ResourceTable::validateLocalizations(void) const char* start = allConfigs; const char* comma; + set<String8> missingConfigs; + AaptLocaleValue locale; do { String8 config; comma = strchr(start, ','); @@ -2626,27 +2674,38 @@ ResourceTable::validateLocalizations(void) config.setTo(start); } - // don't bother with the pseudolocale "zz_ZZ" - if (config != "zz_ZZ") { - if (configSet.find(config) == configSet.end()) { + if (!locale.initFromFilterString(config)) { + continue; + } + + // don't bother with the pseudolocale "en_XA" or "ar_XB" + if (config != "en_XA" && config != "ar_XB") { + if (configSrcMap.find(config) == configSrcMap.end()) { // okay, no specific localization found. it's possible that we are // requiring a specific regional localization [e.g. de_DE] but there is an // available string in the generic language localization [e.g. de]; // consider that string to have fulfilled the localization requirement. String8 region(config.string(), 2); - if (configSet.find(region) == configSet.end()) { - if (configSet.count(defaultLocale) == 0) { - fprintf(stdout, "aapt: warning: " - "**** string '%s' has no default or required localization " - "for '%s' in %s\n", - String8(nameIter->first).string(), - config.string(), - mBundle->getResourceSourceDirs()[0]); - } + if (configSrcMap.find(region) == configSrcMap.end() && + configSrcMap.count(defaultLocale) == 0) { + missingConfigs.insert(config); } } } - } while (comma != NULL); + } while (comma != NULL); + + if (!missingConfigs.empty()) { + String8 configStr; + for (set<String8>::iterator iter = missingConfigs.begin(); + iter != missingConfigs.end(); + iter++) { + configStr.appendFormat(" %s", iter->string()); + } + SourcePos().warning("string '%s' is missing %u required localizations:%s", + String8(nameIter->first).string(), + (unsigned int)missingConfigs.size(), + configStr.string()); + } } } diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h index a3e0666..75005cd 100644 --- a/tools/aapt/ResourceTable.h +++ b/tools/aapt/ResourceTable.h @@ -220,7 +220,7 @@ public: status_t assignResourceIds(); status_t addSymbols(const sp<AaptSymbols>& outSymbols = NULL); - void addLocalization(const String16& name, const String8& locale); + void addLocalization(const String16& name, const String8& locale, const SourcePos& src); status_t validateLocalizations(void); status_t flatten(Bundle*, const sp<AaptFile>& dest); @@ -551,7 +551,7 @@ private: Bundle* mBundle; // key = string resource name, value = set of locales in which that name is defined - map<String16, set<String8> > mLocalizations; + map<String16, map<String8, SourcePos> > mLocalizations; }; #endif diff --git a/tools/aapt/SourcePos.cpp b/tools/aapt/SourcePos.cpp index e2a921c..ae25047 100644 --- a/tools/aapt/SourcePos.cpp +++ b/tools/aapt/SourcePos.cpp @@ -10,17 +10,20 @@ using namespace std; // ============================================================================= struct ErrorPos { + enum Level { + NOTE, + WARNING, + ERROR + }; + String8 file; int line; String8 error; - bool fatal; + Level level; ErrorPos(); ErrorPos(const ErrorPos& that); - ErrorPos(const String8& file, int line, const String8& error, bool fatal); - ~ErrorPos(); - bool operator<(const ErrorPos& rhs) const; - bool operator==(const ErrorPos& rhs) const; + ErrorPos(const String8& file, int line, const String8& error, Level level); ErrorPos& operator=(const ErrorPos& rhs); void print(FILE* to) const; @@ -29,7 +32,7 @@ struct ErrorPos static vector<ErrorPos> g_errors; ErrorPos::ErrorPos() - :line(-1), fatal(false) + :line(-1), level(NOTE) { } @@ -37,41 +40,16 @@ ErrorPos::ErrorPos(const ErrorPos& that) :file(that.file), line(that.line), error(that.error), - fatal(that.fatal) + level(that.level) { } -ErrorPos::ErrorPos(const String8& f, int l, const String8& e, bool fat) +ErrorPos::ErrorPos(const String8& f, int l, const String8& e, Level lev) :file(f), line(l), error(e), - fatal(fat) -{ -} - -ErrorPos::~ErrorPos() -{ -} - -bool -ErrorPos::operator<(const ErrorPos& rhs) const -{ - if (this->file < rhs.file) return true; - if (this->file == rhs.file) { - if (this->line < rhs.line) return true; - if (this->line == rhs.line) { - if (this->error < rhs.error) return true; - } - } - return false; -} - -bool -ErrorPos::operator==(const ErrorPos& rhs) const + level(lev) { - return this->file == rhs.file - && this->line == rhs.line - && this->error == rhs.error; } ErrorPos& @@ -80,18 +58,34 @@ ErrorPos::operator=(const ErrorPos& rhs) this->file = rhs.file; this->line = rhs.line; this->error = rhs.error; + this->level = rhs.level; return *this; } void ErrorPos::print(FILE* to) const { - const char* type = fatal ? "error:" : "warning:"; + const char* type = ""; + switch (level) { + case NOTE: + type = "note: "; + break; + case WARNING: + type = "warning: "; + break; + case ERROR: + type = "error: "; + break; + } - if (this->line >= 0) { - fprintf(to, "%s:%d: %s %s\n", this->file.string(), this->line, type, this->error.string()); + if (!this->file.isEmpty()) { + if (this->line >= 0) { + fprintf(to, "%s:%d: %s%s\n", this->file.string(), this->line, type, this->error.string()); + } else { + fprintf(to, "%s: %s%s\n", this->file.string(), type, this->error.string()); + } } else { - fprintf(to, "%s: %s %s\n", this->file.string(), type, this->error.string()); + fprintf(to, "%s%s\n", type, this->error.string()); } } @@ -116,40 +110,34 @@ SourcePos::~SourcePos() { } -int +void SourcePos::error(const char* fmt, ...) const { - int retval=0; - char buf[1024]; va_list ap; va_start(ap, fmt); - retval = vsnprintf(buf, sizeof(buf), fmt, ap); + String8 msg = String8::formatV(fmt, ap); va_end(ap); - char* p = buf + retval - 1; - while (p > buf && *p == '\n') { - *p = '\0'; - p--; - } - g_errors.push_back(ErrorPos(this->file, this->line, String8(buf), true)); - return retval; + g_errors.push_back(ErrorPos(this->file, this->line, msg, ErrorPos::ERROR)); } -int +void SourcePos::warning(const char* fmt, ...) const { - int retval=0; - char buf[1024]; va_list ap; va_start(ap, fmt); - retval = vsnprintf(buf, sizeof(buf), fmt, ap); + String8 msg = String8::formatV(fmt, ap); va_end(ap); - char* p = buf + retval - 1; - while (p > buf && *p == '\n') { - *p = '\0'; - p--; - } - ErrorPos(this->file, this->line, String8(buf), false).print(stderr); - return retval; + ErrorPos(this->file, this->line, msg, ErrorPos::WARNING).print(stderr); +} + +void +SourcePos::printf(const char* fmt, ...) const +{ + va_list ap; + va_start(ap, fmt); + String8 msg = String8::formatV(fmt, ap); + va_end(ap); + ErrorPos(this->file, this->line, msg, ErrorPos::NOTE).print(stderr); } bool diff --git a/tools/aapt/SourcePos.h b/tools/aapt/SourcePos.h index 33f72a9..4ce817f 100644 --- a/tools/aapt/SourcePos.h +++ b/tools/aapt/SourcePos.h @@ -17,8 +17,9 @@ public: SourcePos(); ~SourcePos(); - int error(const char* fmt, ...) const; - int warning(const char* fmt, ...) const; + void error(const char* fmt, ...) const; + void warning(const char* fmt, ...) const; + void printf(const char* fmt, ...) const; static bool hasErrors(); static void printErrors(FILE* to); diff --git a/tools/aapt/XMLNode.cpp b/tools/aapt/XMLNode.cpp index a663ad5..607d419 100644 --- a/tools/aapt/XMLNode.cpp +++ b/tools/aapt/XMLNode.cpp @@ -187,7 +187,7 @@ status_t parseStyledString(Bundle* bundle, String16* outString, Vector<StringPool::entry_style_span>* outSpans, bool isFormatted, - bool pseudolocalize) + PseudolocalizationMethod pseudolocalize) { Vector<StringPool::entry_style_span> spanStack; String16 curString; @@ -198,21 +198,30 @@ status_t parseStyledString(Bundle* bundle, size_t len; ResXMLTree::event_code_t code; + // Bracketing if pseudolocalization accented method specified. + if (pseudolocalize == PSEUDO_ACCENTED) { + curString.append(String16(String8("["))); + } while ((code=inXml->next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { - if (code == ResXMLTree::TEXT) { String16 text(inXml->getText(&len)); if (firstTime && text.size() > 0) { firstTime = false; if (text.string()[0] == '@') { // If this is a resource reference, don't do the pseudoloc. - pseudolocalize = false; + pseudolocalize = NO_PSEUDOLOCALIZATION; } } - if (xliffDepth == 0 && pseudolocalize) { - std::string orig(String8(text).string()); - std::string pseudo = pseudolocalize_string(orig); - curString.append(String16(String8(pseudo.c_str()))); + if (xliffDepth == 0 && pseudolocalize > 0) { + String16 pseudo; + if (pseudolocalize == PSEUDO_ACCENTED) { + pseudo = pseudolocalize_string(text); + } else if (pseudolocalize == PSEUDO_BIDI) { + pseudo = pseudobidi_string(text); + } else { + pseudo = text; + } + curString.append(pseudo); } else { if (isFormatted && hasSubstitutionErrors(fileName, inXml, text) != NO_ERROR) { return UNKNOWN_ERROR; @@ -352,6 +361,25 @@ moveon: } } + // Bracketing if pseudolocalization accented method specified. + if (pseudolocalize == PSEUDO_ACCENTED) { + const char16_t* str = outString->string(); + const char16_t* p = str; + const char16_t* e = p + outString->size(); + int words_cnt = 0; + while (p < e) { + if (isspace(*p)) { + words_cnt++; + } + p++; + } + unsigned int length = words_cnt > 3 ? outString->size() : + outString->size() / 2; + curString.append(String16(String8(" "))); + curString.append(pseudo_generate_expansion(length)); + curString.append(String16(String8("]"))); + } + if (code == ResXMLTree::BAD_DOCUMENT) { SourcePos(String8(fileName), inXml->getLineNumber()).error( "Error parsing XML\n"); diff --git a/tools/aapt/XMLNode.h b/tools/aapt/XMLNode.h index 05624b7..ccbf9f4 100644 --- a/tools/aapt/XMLNode.h +++ b/tools/aapt/XMLNode.h @@ -26,7 +26,7 @@ status_t parseStyledString(Bundle* bundle, String16* outString, Vector<StringPool::entry_style_span>* outSpans, bool isFormatted, - bool isPseudolocalizable); + PseudolocalizationMethod isPseudolocalizable); void printXMLBlock(ResXMLTree* block); diff --git a/tools/aapt/pseudolocalize.cpp b/tools/aapt/pseudolocalize.cpp index 9e50c5a..60aa2b2 100644 --- a/tools/aapt/pseudolocalize.cpp +++ b/tools/aapt/pseudolocalize.cpp @@ -2,89 +2,155 @@ using namespace std; +// String basis to generate expansion +static const String16 k_expansion_string = String16("one two three " + "four five six seven eight nine ten eleven twelve thirteen " + "fourteen fiveteen sixteen seventeen nineteen twenty"); + +// Special unicode characters to override directionality of the words +static const String16 k_rlm = String16("\xe2\x80\x8f"); +static const String16 k_rlo = String16("\xE2\x80\xae"); +static const String16 k_pdf = String16("\xE2\x80\xac"); + +// Placeholder marks +static const String16 k_placeholder_open = String16("\xc2\xbb"); +static const String16 k_placeholder_close = String16("\xc2\xab"); + static const char* -pseudolocalize_char(char c) +pseudolocalize_char(const char16_t c) { switch (c) { - case 'a': return "\xc4\x83"; - case 'b': return "\xcf\x84"; - case 'c': return "\xc4\x8b"; - case 'd': return "\xc4\x8f"; - case 'e': return "\xc4\x99"; + case 'a': return "\xc3\xa5"; + case 'b': return "\xc9\x93"; + case 'c': return "\xc3\xa7"; + case 'd': return "\xc3\xb0"; + case 'e': return "\xc3\xa9"; case 'f': return "\xc6\x92"; case 'g': return "\xc4\x9d"; - case 'h': return "\xd1\x9b"; - case 'i': return "\xcf\x8a"; + case 'h': return "\xc4\xa5"; + case 'i': return "\xc3\xae"; case 'j': return "\xc4\xb5"; - case 'k': return "\xc4\xb8"; - case 'l': return "\xc4\xba"; + case 'k': return "\xc4\xb7"; + case 'l': return "\xc4\xbc"; case 'm': return "\xe1\xb8\xbf"; - case 'n': return "\xd0\xb8"; - case 'o': return "\xcf\x8c"; - case 'p': return "\xcf\x81"; + case 'n': return "\xc3\xb1"; + case 'o': return "\xc3\xb6"; + case 'p': return "\xc3\xbe"; case 'q': return "\x51"; - case 'r': return "\xd2\x91"; + case 'r': return "\xc5\x95"; case 's': return "\xc5\xa1"; - case 't': return "\xd1\x82"; - case 'u': return "\xce\xb0"; + case 't': return "\xc5\xa3"; + case 'u': return "\xc3\xbb"; case 'v': return "\x56"; - case 'w': return "\xe1\xba\x85"; + case 'w': return "\xc5\xb5"; case 'x': return "\xd1\x85"; - case 'y': return "\xe1\xbb\xb3"; - case 'z': return "\xc5\xba"; + case 'y': return "\xc3\xbd"; + case 'z': return "\xc5\xbe"; case 'A': return "\xc3\x85"; case 'B': return "\xce\xb2"; - case 'C': return "\xc4\x88"; - case 'D': return "\xc4\x90"; - case 'E': return "\xd0\x84"; - case 'F': return "\xce\x93"; - case 'G': return "\xc4\x9e"; - case 'H': return "\xc4\xa6"; - case 'I': return "\xd0\x87"; - case 'J': return "\xc4\xb5"; + case 'C': return "\xc3\x87"; + case 'D': return "\xc3\x90"; + case 'E': return "\xc3\x89"; + case 'G': return "\xc4\x9c"; + case 'H': return "\xc4\xa4"; + case 'I': return "\xc3\x8e"; + case 'J': return "\xc4\xb4"; case 'K': return "\xc4\xb6"; - case 'L': return "\xc5\x81"; + case 'L': return "\xc4\xbb"; case 'M': return "\xe1\xb8\xbe"; - case 'N': return "\xc5\x83"; - case 'O': return "\xce\x98"; - case 'P': return "\xcf\x81"; + case 'N': return "\xc3\x91"; + case 'O': return "\xc3\x96"; + case 'P': return "\xc3\x9e"; case 'Q': return "\x71"; - case 'R': return "\xd0\xaf"; - case 'S': return "\xc8\x98"; - case 'T': return "\xc5\xa6"; - case 'U': return "\xc5\xa8"; + case 'R': return "\xc5\x94"; + case 'S': return "\xc5\xa0"; + case 'T': return "\xc5\xa2"; + case 'U': return "\xc3\x9b"; case 'V': return "\xce\xbd"; - case 'W': return "\xe1\xba\x84"; + case 'W': return "\xc5\xb4"; case 'X': return "\xc3\x97"; - case 'Y': return "\xc2\xa5"; + case 'Y': return "\xc3\x9d"; case 'Z': return "\xc5\xbd"; + case '!': return "\xc2\xa1"; + case '?': return "\xc2\xbf"; + case '$': return "\xe2\x82\xac"; default: return NULL; } } +static bool +is_possible_normal_placeholder_end(const char16_t c) { + switch (c) { + case 's': return true; + case 'S': return true; + case 'c': return true; + case 'C': return true; + case 'd': return true; + case 'o': return true; + case 'x': return true; + case 'X': return true; + case 'f': return true; + case 'e': return true; + case 'E': return true; + case 'g': return true; + case 'G': return true; + case 'a': return true; + case 'A': return true; + case 'b': return true; + case 'B': return true; + case 'h': return true; + case 'H': return true; + case '%': return true; + case 'n': return true; + default: return false; + } +} + +String16 +pseudo_generate_expansion(const unsigned int length) { + String16 result = k_expansion_string; + const char16_t* s = result.string(); + if (result.size() < length) { + result += String16(" "); + result += pseudo_generate_expansion(length - result.size()); + } else { + int ext = 0; + // Should contain only whole words, so looking for a space + for (unsigned int i = length + 1; i < result.size(); ++i) { + ++ext; + if (s[i] == ' ') { + break; + } + } + result.remove(length + ext, 0); + } + return result; +} + /** * Converts characters so they look like they've been localized. * * Note: This leaves escape sequences untouched so they can later be * processed by ResTable::collectString in the normal way. */ -string -pseudolocalize_string(const string& source) +String16 +pseudolocalize_string(const String16& source) { - const char* s = source.c_str(); - string result; - const size_t I = source.length(); + const char16_t* s = source.string(); + String16 result; + const size_t I = source.size(); for (size_t i=0; i<I; i++) { - char c = s[i]; + char16_t c = s[i]; if (c == '\\') { + // Escape syntax, no need to pseudolocalize if (i<I-1) { - result += '\\'; + result += String16("\\"); i++; c = s[i]; switch (c) { case 'u': // this one takes up 5 chars - result += string(s+i, 5); + result += String16(s+i, 5); i += 4; break; case 't': @@ -96,24 +162,107 @@ pseudolocalize_string(const string& source) case '\'': case '\\': default: - result += c; + result.append(&c, 1); break; } } else { - result += c; + result.append(&c, 1); + } + } else if (c == '%') { + // Placeholder syntax, no need to pseudolocalize + result += k_placeholder_open; + bool end = false; + result.append(&c, 1); + while (!end && i < I) { + ++i; + c = s[i]; + result.append(&c, 1); + if (is_possible_normal_placeholder_end(c)) { + end = true; + } else if (c == 't') { + ++i; + c = s[i]; + result.append(&c, 1); + end = true; + } + } + result += k_placeholder_close; + } else if (c == '<' || c == '&') { + // html syntax, no need to pseudolocalize + bool tag_closed = false; + while (!tag_closed && i < I) { + if (c == '&') { + String16 escape_text; + escape_text.append(&c, 1); + bool end = false; + size_t htmlCodePos = i; + while (!end && htmlCodePos < I) { + ++htmlCodePos; + c = s[htmlCodePos]; + escape_text.append(&c, 1); + // Valid html code + if (c == ';') { + end = true; + i = htmlCodePos; + } + // Wrong html code + else if (!((c == '#' || + (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9')))) { + end = true; + } + } + result += escape_text; + if (escape_text != String16("<")) { + tag_closed = true; + } + continue; + } + if (c == '>') { + tag_closed = true; + result.append(&c, 1); + continue; + } + result.append(&c, 1); + i++; + c = s[i]; } } else { + // This is a pure text that should be pseudolocalized const char* p = pseudolocalize_char(c); if (p != NULL) { - result += p; + result += String16(p); } else { - result += c; + result.append(&c, 1); } } } - - //printf("result=\'%s\'\n", result.c_str()); return result; } +String16 +pseudobidi_string(const String16& source) +{ + const char16_t* s = source.string(); + String16 result; + result += k_rlm; + result += k_rlo; + for (size_t i=0; i<source.size(); i++) { + char16_t c = s[i]; + switch(c) { + case ' ': result += k_pdf; + result += k_rlm; + result.append(&c, 1); + result += k_rlm; + result += k_rlo; + break; + default: result.append(&c, 1); + break; + } + } + result += k_pdf; + result += k_rlm; + return result; +} diff --git a/tools/aapt/pseudolocalize.h b/tools/aapt/pseudolocalize.h index 94cb034..e6ab18e 100644 --- a/tools/aapt/pseudolocalize.h +++ b/tools/aapt/pseudolocalize.h @@ -1,9 +1,18 @@ #ifndef HOST_PSEUDOLOCALIZE_H #define HOST_PSEUDOLOCALIZE_H +#include "StringPool.h" + #include <string> -std::string pseudolocalize_string(const std::string& source); +String16 pseudolocalize_string(const String16& source); +// Surrounds every word in the sentance with specific characters that makes +// the word directionality RTL. +String16 pseudobidi_string(const String16& source); +// Generates expansion string based on the specified lenght. +// Generated string could not be shorter that length, but it could be slightly +// longer. +String16 pseudo_generate_expansion(const unsigned int length); #endif // HOST_PSEUDOLOCALIZE_H diff --git a/tools/aidl/Type.cpp b/tools/aidl/Type.cpp index d572af6..2267750 100644 --- a/tools/aidl/Type.cpp +++ b/tools/aidl/Type.cpp @@ -1,5 +1,7 @@ #include "Type.h" +#include <sys/types.h> + Namespace NAMES; Type* VOID_TYPE; diff --git a/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java b/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java index 7b444aa..224eac6 100644 --- a/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java +++ b/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java @@ -36,24 +36,24 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate; /*package*/ class PropertyValuesHolder_Delegate { @LayoutlibDelegate - /*package*/ static int nGetIntMethod(Class<?> targetClass, String methodName) { + /*package*/ static long nGetIntMethod(Class<?> targetClass, String methodName) { // return 0 to force PropertyValuesHolder to use Java reflection. return 0; } @LayoutlibDelegate - /*package*/ static int nGetFloatMethod(Class<?> targetClass, String methodName) { + /*package*/ static long nGetFloatMethod(Class<?> targetClass, String methodName) { // return 0 to force PropertyValuesHolder to use Java reflection. return 0; } @LayoutlibDelegate - /*package*/ static void nCallIntMethod(Object target, int methodID, int arg) { + /*package*/ static void nCallIntMethod(Object target, long methodID, int arg) { // do nothing } @LayoutlibDelegate - /*package*/ static void nCallFloatMethod(Object target, int methodID, float arg) { + /*package*/ static void nCallFloatMethod(Object target, long methodID, float arg) { // 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 7df21ce..5c2b793 100644 --- a/tools/layoutlib/bridge/src/android/content/res/AssetManager_Delegate.java +++ b/tools/layoutlib/bridge/src/android/content/res/AssetManager_Delegate.java @@ -28,18 +28,18 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate; public class AssetManager_Delegate { @LayoutlibDelegate - /*package*/ static int newTheme(AssetManager manager) { - return (int) Resources_Theme_Delegate.getDelegateManager() + /*package*/ static long newTheme(AssetManager manager) { + return Resources_Theme_Delegate.getDelegateManager() .addNewDelegate(new Resources_Theme_Delegate()); } @LayoutlibDelegate - /*package*/ static void deleteTheme(AssetManager manager, int theme) { + /*package*/ static void deleteTheme(AssetManager manager, long theme) { Resources_Theme_Delegate.getDelegateManager().removeJavaReferenceFor(theme); } @LayoutlibDelegate - /*package*/ static void applyThemeStyle(int theme, int styleRes, boolean force) { + /*package*/ static void applyThemeStyle(long theme, int styleRes, boolean force) { Resources_Theme_Delegate delegate = Resources_Theme_Delegate.getDelegateManager() .getDelegate(theme); delegate.mThemeResId = styleRes; diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java index d85c3d1..06673c1 100644 --- a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java @@ -102,7 +102,7 @@ import java.util.Set; } @LayoutlibDelegate - /*package*/ static Bitmap nativeDecodeAsset(int asset, Rect padding, Options opts) { + /*package*/ static Bitmap nativeDecodeAsset(long asset, Rect padding, Options opts) { opts.inBitmap = null; return null; } diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java index 9d21866..73d274c 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java @@ -786,7 +786,7 @@ public final class Canvas_Delegate { } @LayoutlibDelegate - /*package*/ static void native_drawPath(long nativeCanvas, int path, long paint) { + /*package*/ static void native_drawPath(long nativeCanvas, long path, long paint) { final Path_Delegate pathDelegate = Path_Delegate.getDelegate(path); if (pathDelegate == null) { return; diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java index 1d66586..ebfe9bc 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java @@ -654,7 +654,7 @@ public final class Matrix_Delegate { } @LayoutlibDelegate - /*package*/ static boolean native_invert(long native_object, int inverse) { + /*package*/ static boolean native_invert(long native_object, long inverse) { Matrix_Delegate d = sManager.getDelegate(native_object); if (d == null) { return false; diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java index 4e9c129..6f6ef20 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java @@ -401,17 +401,17 @@ public final class Path_Delegate { } @LayoutlibDelegate - /*package*/ static void native_addPath(long nPath, int src, float dx, float dy) { + /*package*/ static void native_addPath(long nPath, long src, float dx, float dy) { addPath(nPath, src, AffineTransform.getTranslateInstance(dx, dy)); } @LayoutlibDelegate - /*package*/ static void native_addPath(long nPath, int src) { + /*package*/ static void native_addPath(long nPath, long src) { addPath(nPath, src, null /*transform*/); } @LayoutlibDelegate - /*package*/ static void native_addPath(long nPath, int src, long matrix) { + /*package*/ static void native_addPath(long nPath, long src, long matrix) { Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(matrix); if (matrixDelegate == null) { return; @@ -474,7 +474,7 @@ public final class Path_Delegate { } @LayoutlibDelegate - /*package*/ static boolean native_op(long nPath1, long nPath2, int op, int result) { + /*package*/ static boolean native_op(long nPath1, long nPath2, int op, long result) { Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, "Path.op() not supported", null); return false; } diff --git a/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java b/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java index fd594f7..5f0d98b 100644 --- a/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java +++ b/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java @@ -33,11 +33,6 @@ public class SystemClock_Delegate { private static long sBootTime = System.currentTimeMillis(); private static long sBootTimeNano = System.nanoTime(); - @LayoutlibDelegate - /*package*/ static boolean setCurrentTimeMillis(long millis) { - return true; - } - /** * Returns milliseconds since boot, not counting time spent in deep sleep. * <b>Note:</b> This value may get reset occasionally (before it would diff --git a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java index ad4103b..0f66fd7 100644 --- a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java +++ b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java @@ -137,6 +137,11 @@ public class ICU_Delegate { } @LayoutlibDelegate + /*package*/ static String getDisplayScriptNative(String variantCode, String locale) { + return ""; + } + + @LayoutlibDelegate /*package*/ static String getISO3CountryNative(String locale) { return ""; } @@ -166,6 +171,17 @@ public class ICU_Delegate { return Locale.getISOCountries(); } + + @LayoutlibDelegate + /*package*/ static String localeForLanguageTag(String languageTag, boolean strict) { + return ""; + } + + @LayoutlibDelegate + /*package*/ static String languageTagForLocale(String locale) { + return ""; + } + @LayoutlibDelegate /*package*/ static boolean initLocaleDataNative(String locale, LocaleData result) { @@ -215,7 +231,7 @@ public class ICU_Delegate { result.percent = '%'; result.perMill = '\u2030'; result.monetarySeparator = ' '; - result.minusSign = '-'; + result.minusSign = "-"; result.exponentSeparator = "e"; result.infinity = "\u221E"; result.NaN = "NaN"; @@ -231,4 +247,14 @@ public class ICU_Delegate { return true; } + + @LayoutlibDelegate + /*package*/ static void setDefaultLocale(String locale) { + ICU.setDefaultLocale(locale); + } + + @LayoutlibDelegate + /*package*/ static String getDefaultLocale() { + return ICU.getDefaultLocale(); + } } diff --git a/tools/preload/Android.mk b/tools/preload/Android.mk index f325870..14a4547 100644 --- a/tools/preload/Android.mk +++ b/tools/preload/Android.mk @@ -20,4 +20,4 @@ LOCAL_MODULE:= preload include $(BUILD_HOST_JAVA_LIBRARY) -include $(call all-subdir-makefiles) +include $(call all-makefiles-under,$(LOCAL_PATH)) |