From: Eric Fischer Date: Wed, 15 Sep 2010 22:59:21 +0000 (-0700) Subject: Add an aapt option to allow string variations for different devices. X-Git-Url: https://git.saurik.com/android/aapt.git/commitdiff_plain/b8ea3a3f81e78ebbe5045af5f77175e3722b4d05?hp=--cc Add an aapt option to allow string variations for different devices. The --product option to aapt is a comma-separated list of characteristics of the device being built for. For example, --product nosdcard,grayscale for a device with no SD card and a grayscale screen. Strings can specify a product="characteristic" option to cause that version of the string to be used only for that type of device. All such strings should also specify, at the end of the block, product="default", which will be used if none of the variations match. For example: Choose black or white Choose a shade of gray Choose a color The default characteristic will also be used when no --product option is specified. Change-Id: Ie6c1505599e02e15b7818e8be6ec47bc6ce71aaa --- b8ea3a3f81e78ebbe5045af5f77175e3722b4d05 diff --git a/Bundle.h b/Bundle.h index a1bc241..c5aa573 100644 --- a/Bundle.h +++ b/Bundle.h @@ -45,7 +45,7 @@ public: mRClassDir(NULL), mResourceIntermediatesDir(NULL), mManifestMinSdkVersion(NULL), mMinSdkVersion(NULL), mTargetSdkVersion(NULL), mMaxSdkVersion(NULL), mVersionCode(NULL), mVersionName(NULL), mCustomPackage(NULL), - mMaxResVersion(NULL), mDebugMode(false), + mMaxResVersion(NULL), mDebugMode(false), mProduct(NULL), mArgc(0), mArgv(NULL) {} ~Bundle(void) {} @@ -139,6 +139,8 @@ public: void setMaxResVersion(const char * val) { mMaxResVersion = val; } bool getDebugMode() { return mDebugMode; } void setDebugMode(bool val) { mDebugMode = val; } + const char* getProduct() const { return mProduct; } + void setProduct(const char * val) { mProduct = val; } /* * Set and get the file specification. @@ -237,6 +239,7 @@ private: const char* mCustomPackage; const char* mMaxResVersion; bool mDebugMode; + const char* mProduct; /* file specification */ int mArgc; diff --git a/Main.cpp b/Main.cpp index 71c023d..739b01f 100644 --- a/Main.cpp +++ b/Main.cpp @@ -67,6 +67,7 @@ void usage(void) " [-A asset-source-dir] [-G class-list-file] [-P public-definitions-file] \\\n" " [-S resource-sources [-S resource-sources ...]] " " [-F apk-file] [-J R-file-dir] \\\n" + " [--product product1,product2,...] \\\n" " [raw-files-dir [raw-files-dir] ...]\n" "\n" " Package the android resources. It will read assets and resources that are\n" @@ -154,6 +155,9 @@ void usage(void) " components target the given package. Useful when used in\n" " conjunction with --rename-manifest-package to fix tests against\n" " a package that has been renamed.\n" + " --product\n" + " Specifies which variant to choose for strings that have\n" + " product variants\n" " --utf16\n" " changes default encoding for resources to UTF-16. Only useful when API\n" " level is set to 7 or higher where the default encoding is UTF-8.\n"); @@ -484,6 +488,15 @@ int main(int argc, char* const argv[]) bundle.setInstrumentationPackageNameOverride(argv[0]); } else if (strcmp(cp, "-auto-add-overlay") == 0) { bundle.setAutoAddOverlay(true); + } else if (strcmp(cp, "-product") == 0) { + argc--; + argv++; + if (!argc) { + fprintf(stderr, "ERROR: No argument supplied for '--product' option\n"); + wantUsage = true; + goto bail; + } + bundle.setProduct(argv[0]); } else { fprintf(stderr, "ERROR: Unknown option '-%s'\n", cp); wantUsage = true; diff --git a/ResourceTable.cpp b/ResourceTable.cpp index 755b93b..532fb65 100644 --- a/ResourceTable.cpp +++ b/ResourceTable.cpp @@ -574,6 +574,7 @@ status_t parseAndAddBag(Bundle* bundle, const String16& itemIdent, int32_t curFormat, bool isFormatted, + const String16& product, bool pseudolocalize, const bool overwrite, ResourceTable* outTable) @@ -606,6 +607,32 @@ status_t parseAndAddBag(Bundle* bundle, return err; } +/* + * Returns true if needle is one of the elements in the comma-separated list + * haystack, false otherwise. + */ +bool isInProductList(const String16& needle, const String16& haystack) { + const char16_t *needle2 = needle.string(); + const char16_t *haystack2 = haystack.string(); + size_t needlesize = needle.size(); + + while (*haystack2 != '\0') { + if (strncmp16(haystack2, needle2, needlesize) == 0) { + if (haystack2[needlesize] == '\0' || haystack2[needlesize] == ',') { + return true; + } + } + + while (*haystack2 != '\0' && *haystack2 != ',') { + haystack2++; + } + if (*haystack2 == ',') { + haystack2++; + } + } + + return false; +} status_t parseAndAddEntry(Bundle* bundle, const sp& in, @@ -618,6 +645,7 @@ status_t parseAndAddEntry(Bundle* bundle, bool curIsStyled, int32_t curFormat, bool isFormatted, + const String16& product, bool pseudolocalize, const bool overwrite, ResourceTable* outTable) @@ -634,6 +662,47 @@ status_t parseAndAddEntry(Bundle* bundle, return err; } + /* + * If a product type was specified on the command line + * and also in the string, and the two are not the same, + * return without adding the string. + */ + + const char *bundleProduct = bundle->getProduct(); + if (bundleProduct == NULL) { + bundleProduct = ""; + } + + if (product.size() != 0) { + /* + * If the command-line-specified product is empty, only "default" + * matches. Other variants are skipped. This is so generation + * of the R.java file when the product is not known is predictable. + */ + + if (bundleProduct[0] == '\0') { + if (strcmp16(String16("default").string(), product.string()) != 0) { + return NO_ERROR; + } + } else { + /* + * The command-line product is not empty. + * If the product for this string is on the command-line list, + * it matches. "default" also matches, but only if nothing + * else has matched already. + */ + + if (isInProductList(product, String16(bundleProduct))) { + ; + } else if (strcmp16(String16("default").string(), product.string()) == 0 && + !outTable->hasBagOrEntry(myPackage, curType, ident)) { + ; + } else { + return NO_ERROR; + } + } + } + NOISY(printf("Adding resource entry l=%c%c c=%c%c orien=%d d=%d id=%s: %s\n", config.language[0], config.language[1], config.country[0], config.country[1], @@ -713,6 +782,7 @@ status_t compileResourceFile(Bundle* bundle, const String16 translatable16("translatable"); const String16 formatted16("formatted"); const String16 false16("false"); + const String16 product16("product"); const String16 myPackage(assets->getPackage()); @@ -760,6 +830,7 @@ status_t compileResourceFile(Bundle* bundle, bool curIsStyled = false; bool curIsPseudolocalizable = false; bool curIsFormatted = fileIsTranslatable; + String16 curProduct; bool localHasErrors = false; if (strcmp16(block.getElementName(&len), skip16.string()) == 0) { @@ -1157,6 +1228,8 @@ status_t compileResourceFile(Bundle* bundle, translatable.setTo(block.getAttributeStringValue(i, &length)); } else if (strcmp16(attr, formatted16.string()) == 0) { formatted.setTo(block.getAttributeStringValue(i, &length)); + } else if (strcmp16(attr, product16.string()) == 0) { + curProduct.setTo(block.getAttributeStringValue(i, &length)); } } @@ -1374,7 +1447,7 @@ status_t compileResourceFile(Bundle* bundle, err = parseAndAddBag(bundle, in, &block, curParams, myPackage, curType, ident, parentIdent, itemIdent, curFormat, curIsFormatted, - false, overwrite, outTable); + curProduct, false, overwrite, outTable); if (err == NO_ERROR) { if (curIsPseudolocalizable && localeIsDefined(curParams) && bundle->getPseudolocalize()) { @@ -1383,7 +1456,7 @@ status_t compileResourceFile(Bundle* bundle, block.setPosition(parserPosition); err = parseAndAddBag(bundle, in, &block, pseudoParams, myPackage, curType, ident, parentIdent, itemIdent, curFormat, - curIsFormatted, true, overwrite, outTable); + curIsFormatted, curProduct, true, overwrite, outTable); #endif } } @@ -1407,7 +1480,7 @@ status_t compileResourceFile(Bundle* bundle, err = parseAndAddEntry(bundle, in, &block, curParams, myPackage, curType, ident, *curTag, curIsStyled, curFormat, curIsFormatted, - false, overwrite, outTable); + curProduct, false, overwrite, outTable); if (err < NO_ERROR) { // Why err < NO_ERROR instead of err != NO_ERROR? hasErrors = localHasErrors = true; @@ -1419,7 +1492,8 @@ status_t compileResourceFile(Bundle* bundle, block.setPosition(parserPosition); err = parseAndAddEntry(bundle, in, &block, pseudoParams, myPackage, curType, ident, *curTag, curIsStyled, curFormat, - curIsFormatted, true, overwrite, outTable); + curIsFormatted, curProduct, + true, overwrite, outTable); if (err != NO_ERROR) { hasErrors = localHasErrors = true; }