diff options
author | Narayan Kamath <narayan@google.com> | 2014-02-10 16:51:39 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2014-02-10 16:51:39 +0000 |
commit | 9edcb7c9080e31f44b0dd13d291a0f94c7001705 (patch) | |
tree | 3f960b4ed879ba5e9a0f97297cbaf30d6f998841 | |
parent | 62cfa91f0ddf02b7d0941f3ebfe475b43871c1ab (diff) | |
parent | 91447d88f2bdf9c2bf8d1a53570efef6172fba74 (diff) | |
download | frameworks_base-9edcb7c9080e31f44b0dd13d291a0f94c7001705.zip frameworks_base-9edcb7c9080e31f44b0dd13d291a0f94c7001705.tar.gz frameworks_base-9edcb7c9080e31f44b0dd13d291a0f94c7001705.tar.bz2 |
Merge "Extended locales in AAPT / AssetManager."
-rw-r--r-- | core/java/android/content/res/Resources.java | 5 | ||||
-rw-r--r-- | include/androidfw/ResourceTypes.h | 41 | ||||
-rw-r--r-- | libs/androidfw/AssetManager.cpp | 30 | ||||
-rw-r--r-- | libs/androidfw/ResourceTypes.cpp | 95 | ||||
-rw-r--r-- | libs/androidfw/tests/ResourceTypes_test.cpp | 35 | ||||
-rw-r--r-- | tools/aapt/AaptAssets.cpp | 583 | ||||
-rw-r--r-- | tools/aapt/AaptAssets.h | 118 | ||||
-rw-r--r-- | tools/aapt/Command.cpp | 1 | ||||
-rw-r--r-- | tools/aapt/Resource.cpp | 1 | ||||
-rw-r--r-- | tools/aapt/ResourceFilter.cpp | 31 | ||||
-rw-r--r-- | tools/aapt/ResourceFilter.h | 7 | ||||
-rw-r--r-- | tools/aapt/ResourceIdCache.h | 1 | ||||
-rw-r--r-- | tools/aapt/ResourceTable.cpp | 4 |
13 files changed, 664 insertions, 288 deletions
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index aa96f0e..de00f71 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -1625,10 +1625,7 @@ public class Resources { String locale = null; if (mConfiguration.locale != null) { - locale = mConfiguration.locale.getLanguage(); - if (mConfiguration.locale.getCountry() != null) { - locale += "-" + mConfiguration.locale.getCountry(); - } + locale = mConfiguration.locale.toLanguageTag(); } int width, height; if (mMetrics.widthPixels >= mMetrics.heightPixels) { diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h index e79011f..610465e 100644 --- a/include/androidfw/ResourceTypes.h +++ b/include/androidfw/ResourceTypes.h @@ -1053,7 +1053,7 @@ struct ResTable_config // The ISO-15924 short name for the script corresponding to this // configuration. (eg. Hant, Latn, etc.). Interpreted in conjunction with - // the locale field + // the locale field. char localeScript[4]; // A single BCP-47 variant subtag. Will vary in length between 5 and 8 @@ -1118,14 +1118,23 @@ struct ResTable_config bool match(const ResTable_config& settings) const; // Get the string representation of the locale component of this - // Config. This will contain the language along with the prefixed script, - // region and variant of this config, separated by underscores. + // Config. The maximum size of this representation will be + // |RESTABLE_MAX_LOCALE_LEN| (including a terminating '\0'). // - // 'r' is the region prefix, 's' is the script prefix and 'v' is the - // variant prefix. - // - // Example: en_rUS, en_sLatn_rUS, en_vPOSIX. - void getLocale(char str[RESTABLE_MAX_LOCALE_LEN]) const; + // Example: en-US, en-Latn-US, en-POSIX. + void getBcp47Locale(char* out) const; + + // Sets the values of language, region, script and variant to the + // well formed BCP-47 locale contained in |in|. The input locale is + // assumed to be valid and no validation is performed. + void setBcp47Locale(const char* in); + + inline void clearLocale() { + locale = 0; + memset(localeScript, 0, sizeof(localeScript)); + memset(localeVariant, 0, sizeof(localeVariant)); + } + // Get the 2 or 3 letter language code of this configuration. Trailing // bytes are set to '\0'. size_t unpackLanguage(char language[4]) const; @@ -1133,12 +1142,16 @@ struct ResTable_config // bytes are set to '\0'. size_t unpackRegion(char region[4]) const; - // Sets the language code of this configuration from |language|. If |language| - // is a 2 letter code, the trailing byte is expected to be '\0'. - void packLanguage(const char language[3]); - // Sets the region code of this configuration from |region|. If |region| - // is a 2 letter code, the trailing byte is expected to be '\0'. - void packRegion(const char region[3]); + // Sets the language code of this configuration to the first three + // chars at |language|. + // + // If |language| is a 2 letter code, the trailing byte must be '\0' or + // the BCP-47 separator '-'. + void packLanguage(const char* language); + // Sets the region code of this configuration to the first three bytes + // at |region|. If |region| is a 2 letter code, the trailing byte must be '\0' + // or the BCP-47 separator '-'. + void packRegion(const char* region); // Returns a positive integer if this config is more specific than |o| // with respect to their locales, a negative integer if |o| is more specific diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp index 501b83e..9cea68c 100644 --- a/libs/androidfw/AssetManager.cpp +++ b/libs/androidfw/AssetManager.cpp @@ -463,17 +463,8 @@ void AssetManager::setConfiguration(const ResTable_config& config, const char* l if (locale) { setLocaleLocked(locale); } else if (config.language[0] != 0) { - char spec[9]; - spec[0] = config.language[0]; - spec[1] = config.language[1]; - if (config.country[0] != 0) { - spec[2] = '_'; - spec[3] = config.country[0]; - spec[4] = config.country[1]; - spec[5] = 0; - } else { - spec[3] = 0; - } + char spec[RESTABLE_MAX_LOCALE_LEN]; + config.getBcp47Locale(spec); setLocaleLocked(spec); } else { updateResourceParamsLocked(); @@ -733,20 +724,11 @@ void AssetManager::updateResourceParamsLocked() const return; } - size_t llen = mLocale ? strlen(mLocale) : 0; - mConfig->language[0] = 0; - mConfig->language[1] = 0; - mConfig->country[0] = 0; - mConfig->country[1] = 0; - if (llen >= 2) { - mConfig->language[0] = mLocale[0]; - mConfig->language[1] = mLocale[1]; - } - if (llen >= 5) { - mConfig->country[0] = mLocale[3]; - mConfig->country[1] = mLocale[4]; + if (mLocale) { + mConfig->setBcp47Locale(mLocale); + } else { + mConfig->clearLocale(); } - mConfig->size = sizeof(*mConfig); res->setParameters(mConfig); } diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 9e544e9..94d150a 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -1566,9 +1566,9 @@ void ResTable_config::copyFromDeviceNoSwap(const ResTable_config& o) { return 0; } -/* static */ void packLanguageOrRegion(const char in[3], const char base, +/* static */ void packLanguageOrRegion(const char* in, const char base, char out[2]) { - if (in[2] == 0) { + if (in[2] == 0 || in[2] == '-') { out[0] = in[0]; out[1] = in[1]; } else { @@ -1582,11 +1582,11 @@ void ResTable_config::copyFromDeviceNoSwap(const ResTable_config& o) { } -void ResTable_config::packLanguage(const char language[3]) { +void ResTable_config::packLanguage(const char* language) { packLanguageOrRegion(language, 'a', this->language); } -void ResTable_config::packRegion(const char region[3]) { +void ResTable_config::packRegion(const char* region) { packLanguageOrRegion(region, '0', this->country); } @@ -2294,7 +2294,7 @@ bool ResTable_config::match(const ResTable_config& settings) const { return true; } -void ResTable_config::getLocale(char str[RESTABLE_MAX_LOCALE_LEN]) const { +void ResTable_config::getBcp47Locale(char str[RESTABLE_MAX_LOCALE_LEN]) const { memset(str, 0, RESTABLE_MAX_LOCALE_LEN); // This represents the "any" locale value, which has traditionally been @@ -2305,34 +2305,83 @@ void ResTable_config::getLocale(char str[RESTABLE_MAX_LOCALE_LEN]) const { size_t charsWritten = 0; if (language[0]) { - unpackLanguage(str); + charsWritten += unpackLanguage(str); } - if (country[0]) { + if (localeScript[0]) { if (charsWritten) { - str[charsWritten++] = '_'; - str[charsWritten++] = 'r'; + str[charsWritten++] = '-'; } - charsWritten += unpackRegion(str + charsWritten); + memcpy(str + charsWritten, localeScript, sizeof(localeScript)); + charsWritten += sizeof(localeScript); } - if (localeScript[0]) { + if (country[0]) { if (charsWritten) { - str[charsWritten++] = '_'; - str[charsWritten++] = '_s'; + str[charsWritten++] = '-'; } - memcpy(str + charsWritten, localeScript, sizeof(localeScript)); + charsWritten += unpackRegion(str + charsWritten); } if (localeVariant[0]) { if (charsWritten) { - str[charsWritten++] = '_'; - str[charsWritten++] = 'v'; + str[charsWritten++] = '-'; } memcpy(str + charsWritten, localeVariant, sizeof(localeVariant)); } } +/* static */ inline bool assignLocaleComponent(ResTable_config* config, + const char* start, size_t size) { + + switch (size) { + case 0: + return false; + case 2: + case 3: + config->language[0] ? config->packRegion(start) : config->packLanguage(start); + break; + case 4: + config->localeScript[0] = toupper(start[0]); + for (size_t i = 1; i < 4; ++i) { + config->localeScript[i] = tolower(start[i]); + } + break; + case 5: + case 6: + case 7: + case 8: + for (size_t i = 0; i < size; ++i) { + config->localeVariant[i] = tolower(start[i]); + } + break; + default: + return false; + } + + return true; +} + +void ResTable_config::setBcp47Locale(const char* in) { + locale = 0; + memset(localeScript, 0, sizeof(localeScript)); + memset(localeVariant, 0, sizeof(localeVariant)); + + const char* separator = in; + const char* start = in; + while ((separator = strchr(start, '-')) != NULL) { + const size_t size = separator - start; + if (!assignLocaleComponent(this, start, size)) { + fprintf(stderr, "Invalid BCP-47 locale string: %s", in); + } + + start = (separator + 1); + } + + const size_t size = in + strlen(in) - start; + assignLocaleComponent(this, start, size); +} + String8 ResTable_config::toString() const { String8 res; @@ -2345,7 +2394,7 @@ String8 ResTable_config::toString() const { res.appendFormat("%dmnc", dtohs(mnc)); } char localeStr[RESTABLE_MAX_LOCALE_LEN]; - getLocale(localeStr); + getBcp47Locale(localeStr); res.append(localeStr); if ((screenLayout&MASK_LAYOUTDIR) != 0) { @@ -5100,18 +5149,20 @@ void ResTable::getConfigurations(Vector<ResTable_config>* configs) const const size_t L = type->configs.size(); for (size_t l=0; l<L; l++) { const ResTable_type* config = type->configs[l]; - const ResTable_config* cfg = &config->config; + ResTable_config cfg; + memset(&cfg, 0, sizeof(ResTable_config)); + cfg.copyFromDtoH(config->config); // only insert unique const size_t M = configs->size(); size_t m; for (m=0; m<M; m++) { - if (0 == (*configs)[m].compare(*cfg)) { + if (0 == (*configs)[m].compare(cfg)) { break; } } // if we didn't find it if (m == M) { - configs->add(*cfg); + configs->add(cfg); } } } @@ -5129,7 +5180,7 @@ void ResTable::getLocales(Vector<String8>* locales) const char locale[RESTABLE_MAX_LOCALE_LEN]; for (size_t i=0; i<I; i++) { - configs[i].getLocale(locale); + configs[i].getBcp47Locale(locale); const size_t J = locales->size(); size_t j; for (j=0; j<J; j++) { @@ -5753,7 +5804,7 @@ void ResTable::print(bool inclValues) const } #if 0 char localeStr[RESTABLE_MAX_LOCALE_LEN]; - mParams.getLocale(localeStr); + mParams.getBcp47Locale(localeStr); printf("mParams=%s,\n" localeStr); #endif size_t pgCount = mPackageGroups.size(); diff --git a/libs/androidfw/tests/ResourceTypes_test.cpp b/libs/androidfw/tests/ResourceTypes_test.cpp index eadfe00..4888b4a 100644 --- a/libs/androidfw/tests/ResourceTypes_test.cpp +++ b/libs/androidfw/tests/ResourceTypes_test.cpp @@ -146,5 +146,40 @@ TEST(ResourceTypesTest, IsMoreSpecificThan) { EXPECT_TRUE(r.isMoreSpecificThan(l)); } +TEST(ResourceTypesTest, setLocale) { + ResTable_config test; + test.setBcp47Locale("en-US"); + EXPECT_EQ('e', test.language[0]); + EXPECT_EQ('n', test.language[1]); + EXPECT_EQ('U', test.country[0]); + EXPECT_EQ('S', test.country[1]); + EXPECT_EQ(0, test.localeScript[0]); + EXPECT_EQ(0, test.localeVariant[0]); + + test.setBcp47Locale("eng-419"); + char out[4] = { 1, 1, 1, 1}; + test.unpackLanguage(out); + EXPECT_EQ('e', out[0]); + EXPECT_EQ('n', out[1]); + EXPECT_EQ('g', out[2]); + EXPECT_EQ(0, out[3]); + memset(out, 1, 4); + test.unpackRegion(out); + EXPECT_EQ('4', out[0]); + EXPECT_EQ('1', out[1]); + EXPECT_EQ('9', out[2]); + + + test.setBcp47Locale("en-Latn-419"); + memset(out, 1, 4); + EXPECT_EQ('e', test.language[0]); + EXPECT_EQ('n', test.language[1]); + + EXPECT_EQ(0, memcmp("Latn", test.localeScript, 4)); + test.unpackRegion(out); + EXPECT_EQ('4', out[0]); + EXPECT_EQ('1', out[1]); + EXPECT_EQ('9', out[2]); +} } // namespace android. diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp index 6a3c506..f9a2d19 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) { @@ -1496,18 +1721,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); @@ -1992,7 +2217,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) { } @@ -2505,9 +2732,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. 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/Command.cpp b/tools/aapt/Command.cpp index 48e3125..8e856b7 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -600,6 +600,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'; diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index a390e42..c923bc2 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; } diff --git a/tools/aapt/ResourceFilter.cpp b/tools/aapt/ResourceFilter.cpp index 8cfd2a5..e8a2be4 100644 --- a/tools/aapt/ResourceFilter.cpp +++ b/tools/aapt/ResourceFilter.cpp @@ -28,8 +28,8 @@ ResourceFilter::parse(const char* arg) mContainsPseudo = 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 +44,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 +75,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 +86,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 +107,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..0d127ba 100644 --- a/tools/aapt/ResourceFilter.h +++ b/tools/aapt/ResourceFilter.h @@ -19,14 +19,15 @@ public: ResourceFilter() : mData(), mContainsPseudo(false) {} status_t parse(const char* arg); bool isEmpty() const; - bool match(int axis, uint32_t value) const; bool match(int axis, const ResTable_config& config) const; bool match(const ResTable_config& config) const; - const SortedVector<uint32_t>* configsForAxis(int axis) const; + const SortedVector<AxisValue>* configsForAxis(int axis) const; inline bool containsPseudo() const { return mContainsPseudo; } private: - KeyedVector<int,SortedVector<uint32_t> > mData; + bool match(int axis, const AxisValue& value) const; + + KeyedVector<int,SortedVector<AxisValue> > mData; bool mContainsPseudo; }; 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 02a74b1..aff0088 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -1292,8 +1292,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; |