]> git.saurik.com Git - android/aapt.git/commitdiff
Code drop from //branches/cupcake/...@124589
authorThe Android Open Source Project <initial-contribution@android.com>
Thu, 18 Dec 2008 02:05:43 +0000 (18:05 -0800)
committerThe Android Open Source Project <initial-contribution@android.com>
Thu, 18 Dec 2008 02:05:43 +0000 (18:05 -0800)
AaptAssets.cpp
Bundle.h
Command.cpp
Images.cpp
Main.cpp
Package.cpp
Resource.cpp
ResourceTable.cpp
ResourceTable.h
XMLNode.cpp
XMLNode.h

index d07e4de565fe6f17ec093d583e5aca51e92db372..027662d84d26ef4caba55a4e09c1192311719c86 100644 (file)
@@ -78,7 +78,7 @@ static bool isHidden(const char *root, const char *path)
         // Skip CVS but don't chatter about it.
         return true;
     } else if (strcasecmp(path, "thumbs.db") == 0
-               || strcasecmp(path, "picassa.ini") == 0) {
+               || strcasecmp(path, "picasa.ini") == 0) {
         // Skip suspected image indexes files.
         type = "index";
     } else if (path[strlen(path)-1] == '~') {
@@ -695,6 +695,9 @@ bool AaptGroupEntry::getKeysHiddenName(const char* name,
     } else if (strcmp(name, "keyshidden") == 0) {
         mask = out->MASK_KEYSHIDDEN;
         value = out->KEYSHIDDEN_YES;
+    } else if (strcmp(name, "keyssoft") == 0) {
+        mask = out->MASK_KEYSHIDDEN;
+        value = out->KEYSHIDDEN_SOFT;
     }
     
     if (mask != 0) {
index 1d7b3adcee0e86393f01c654edab4a65af37fbc2..99fac2fa23892b5755b379dac45f17dee8d0308a 100644 (file)
--- a/Bundle.h
+++ b/Bundle.h
@@ -94,6 +94,8 @@ public:
     void addPackageInclude(const char* file) { mPackageIncludes.add(file); }
     const android::Vector<const char*>& getJarFiles() const { return mJarFiles; }
     void addJarFile(const char* file) { mJarFiles.add(file); }
+    const android::Vector<const char*>& getNoCompressExtensions() const { return mNoCompressExtensions; }
+    void addNoCompressExtension(const char* ext) { mNoCompressExtensions.add(ext); }
 
     /*
      * Set and get the file specification.
@@ -144,6 +146,7 @@ private:
     android::String8 mConfigurations;
     android::Vector<const char*> mPackageIncludes;
     android::Vector<const char*> mJarFiles;
+    android::Vector<const char*> mNoCompressExtensions;
 
     /* file specification */
     int         mArgc;
index c02e2c04c63f2592bb17faa5cb62702c51c06173..9f75d4bec4ad98d7ac8c4209aa420f787b5959e9 100644 (file)
@@ -586,6 +586,18 @@ int doDump(Bundle* bundle)
                            activityIcon.string());
                 }
             }
+            printf("locales:");
+            Vector<String8> locales;
+            res.getLocales(&locales);
+            const size_t N = locales.size();
+            for (size_t i=0; i<N; i++) {
+                const char* localeStr =  locales[i].string();
+                if (localeStr == NULL || strlen(localeStr) == 0) {
+                    localeStr = "--_--";
+                }
+                printf(" '%s'", localeStr);
+            }
+            printf("\n");
         } else if (strcmp("configurations", option) == 0) {
             Vector<ResTable_config> configs;
             res.getConfigurations(&configs);
index 4a766d157740e18b5b18c006a764ce534d63ec31..9d5937c7d6f063cd49988785427f1395c7982c59 100644 (file)
@@ -562,10 +562,16 @@ getout:
 
 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);
     memcpy(newData, data, patchSize);
     Res_png_9patch* outPatch = inPatch->deserialize(newData);
+    // deserialization is done in place, so outPatch == newData
+    assert(outPatch == newData);
     assert(outPatch->numXDivs == inPatch->numXDivs);
     assert(outPatch->numYDivs == inPatch->numYDivs);
     assert(outPatch->paddingLeft == inPatch->paddingLeft);
@@ -581,6 +587,7 @@ static void checkNinePatchSerialization(Res_png_9patch* inPatch,  void * data)
     for (int i = 0; i < outPatch->numColors; i++) {
         assert(outPatch->colors[i] == inPatch->colors[i]);
     }
+    free(newData);
 }
 
 static bool patch_equals(Res_png_9patch& patch1, Res_png_9patch& patch2) {
index d86ad47ea47b0f60dfae4dd9c310a5b2cfbe01c7..a1978da37f366fabd68c7421c12728fb3827838b 100644 (file)
--- a/Main.cpp
+++ b/Main.cpp
@@ -46,6 +46,7 @@ void usage(void)
         "   List contents of Zip-compatible archive.\n\n", gProgName);
     fprintf(stderr,
         " %s d[ump] WHAT file.{apk} [asset [asset ...]]\n"
+        "   badging          Print the label and icon for the app declared in APK.\n"
         "   permissions      Print the permissions from the APK.\n"
         "   resources        Print the resource table from the APK.\n"
         "   configurations   Print the configurations in the APK.\n"
@@ -53,6 +54,7 @@ void usage(void)
         "   xmlstrings       Print the strings of the given compiled xml assets.\n\n", gProgName);
     fprintf(stderr,
         " %s p[ackage] [-f][-u][-m][-v][-x][-M AndroidManifest.xml] \\\n"
+        "        [-0 extension [-0 extension ...]] \\\n"
         "        [-I base-package [-I base-package ...]] \\\n"
         "        [-A asset-source-dir] [-P public-definitions-file] \\\n"
         "        [-S resource-sources] [-F apk-file] [-J R-file-dir] \\\n"
@@ -106,7 +108,9 @@ void usage(void)
         "   -M  specify full path to AndroidManifest.xml to include in zip\n"
         "   -P  specify where to output public resource definitions\n"
         "   -S  directory in which to find resources\n"
-        "   -0  don't compress files we're adding\n");
+        "   -0  specifies an additional extension for which such files will not\n"
+        "       be stored compressed in the .apk.  An empty string means to not\n"
+        "       compress any files at all.\n");
 }
 
 /*
@@ -303,7 +307,18 @@ int main(int argc, char* const argv[])
                 bundle.setResourceSourceDir(argv[0]);
                 break;
             case '0':
-                bundle.setCompressionMethod(ZipEntry::kCompressStored);
+                argc--;
+                argv++;
+                if (!argc) {
+                    fprintf(stderr, "ERROR: No argument supplied for '-e' option\n");
+                    wantUsage = true;
+                    goto bail;
+                }
+                if (argv[0][0] != 0) {
+                    bundle.addNoCompressExtension(argv[0]);
+                } else {
+                    bundle.setCompressionMethod(ZipEntry::kCompressStored);
+                }
                 break;
             default:
                 fprintf(stderr, "ERROR: Unknown flag '-%c'\n", *cp);
index 23f641a98eb7e12a349f28e53f232fa83d52602d..0df460657346c54c84f2cdf3656b4468f6a256d5 100644 (file)
@@ -34,7 +34,7 @@ ssize_t processAssets(Bundle* bundle, ZipFile* zip,
                         const sp<AaptDir>& dir, const AaptGroupEntry& ge);
 bool processFile(Bundle* bundle, ZipFile* zip,
                         const sp<AaptGroup>& group, const sp<AaptFile>& file);
-bool okayToCompress(const String8& pathName);
+bool okayToCompress(Bundle* bundle, const String8& pathName);
 ssize_t processJarFiles(Bundle* bundle, ZipFile* zip);
 
 /*
@@ -327,7 +327,7 @@ bool processFile(Bundle* bundle, ZipFile* zip,
     } else if (!hasData) {
         /* don't compress certain files, e.g. PNGs */
         int compressionMethod = bundle->getCompressionMethod();
-        if (!okayToCompress(storageName)) {
+        if (!okayToCompress(bundle, storageName)) {
             compressionMethod = ZipEntry::kCompressStored;
         }
         result = zip->add(file->getSourceFile().string(), storageName.string(), compressionMethod,
@@ -365,7 +365,7 @@ bool processFile(Bundle* bundle, ZipFile* zip,
  * Determine whether or not we want to try to compress this file based
  * on the file extension.
  */
-bool okayToCompress(const String8& pathName)
+bool okayToCompress(Bundle* bundle, const String8& pathName)
 {
     String8 ext = pathName.getPathExtension();
     int i;
@@ -378,6 +378,19 @@ bool okayToCompress(const String8& pathName)
             return false;
     }
 
+    const android::Vector<const char*>& others(bundle->getNoCompressExtensions());
+    for (i = 0; i < (int)others.size(); i++) {
+        const char* str = others[i];
+        int pos = pathName.length() - strlen(str);
+        if (pos < 0) {
+            continue;
+        }
+        const char* path = pathName.string();
+        if (strcasecmp(path + pos, str) == 0) {
+            return false;
+        }
+    }
+
     return true;
 }
 
index 0826b3249b47afc8c2b06853d66f322cd84e3192..fd6ddb5796ae80734709da2e25ae4bb93a47040f 100644 (file)
@@ -916,6 +916,10 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
         }
     }
 
+    if (table.validateLocalizations()) {
+        hasErrors = true;
+    }
+    
     if (hasErrors) {
         return UNKNOWN_ERROR;
     }
index 6fe196acb515c831098daa395fc556e4f91f55f4..3641458c4f7626022a4075b74955eb017ab0bb2b 100644 (file)
@@ -638,6 +638,7 @@ status_t compileResourceFile(Bundle* bundle,
     const String16 string16("string");
     const String16 drawable16("drawable");
     const String16 color16("color");
+    const String16 bool16("bool");
     const String16 integer16("integer");
     const String16 dimen16("dimen");
     const String16 style16("style");
@@ -671,6 +672,10 @@ status_t compileResourceFile(Bundle* bundle,
     const String16 many16("many");
     const String16 quantityMany16("^many");
 
+    // useful attribute names and special values
+    const String16 name16("name");
+    const String16 translatable16("translatable");
+    const String16 false16("false");
 
     const String16 myPackage(assets->getPackage());
 
@@ -950,6 +955,45 @@ 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);
+                String8 locale(rawLocale);
+                String16 name;
+                String16 translatable;
+
+                size_t n = block.getAttributeCount();
+                for (size_t i = 0; i < n; i++) {
+                    size_t length;
+                    const uint16_t* attr = block.getAttributeName(i, &length);
+                    if (strcmp16(attr, name16.string()) == 0) {
+                        name.setTo(block.getAttributeStringValue(i, &length));
+                    } else if (strcmp16(attr, translatable16.string()) == 0) {
+                        translatable.setTo(block.getAttributeStringValue(i, &length));
+                    }
+                }
+                
+                if (name.size() > 0) {
+                    if (translatable == false16) {
+                        // 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->getResourceSourceDir(),
+                                    locale.string());
+                            // hasErrors = localHasErrors = true;
+                        } else {
+                            // Intentionally empty block:
+                            //
+                            // Don't add untranslatable strings to the localization table; that
+                            // way if we later see localizations of them, they'll be flagged as
+                            // having no default translation.
+                        }
+                    } else {
+                        outTable->addLocalization(name, locale);
+                    }
+                }
+
                 curTag = &string16;
                 curType = string16;
                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_STRING;
@@ -963,6 +1007,10 @@ status_t compileResourceFile(Bundle* bundle,
                 curTag = &color16;
                 curType = color16;
                 curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_COLOR;
+            } else if (strcmp16(block.getElementName(&len), bool16.string()) == 0) {
+                curTag = &bool16;
+                curType = bool16;
+                curFormat = ResTable_map::TYPE_REFERENCE|ResTable_map::TYPE_BOOLEAN;
             } else if (strcmp16(block.getElementName(&len), integer16.string()) == 0) {
                 curTag = &integer16;
                 curType = integer16;
@@ -1553,17 +1601,26 @@ inline uint32_t ResourceTable::getResId(const sp<Package>& p,
 
 uint32_t ResourceTable::getResId(const String16& package,
                                  const String16& type,
-                                 const String16& name) const
+                                 const String16& name,
+                                 bool onlyPublic) const
 {
     sp<Package> p = mPackages.valueFor(package);
     if (p == NULL) return 0;
 
     // First look for this in the included resources...
+    uint32_t specFlags = 0;
     uint32_t rid = mAssets->getIncludedResources()
         .identifierForName(name.string(), name.size(),
                            type.string(), type.size(),
-                           package.string(), package.size());
+                           package.string(), package.size(),
+                           &specFlags);
     if (rid != 0) {
+        if (onlyPublic) {
+            if ((specFlags & ResTable_typeSpec::SPEC_PUBLIC) == 0) {
+                return 0;
+            }
+        }
+        
         if (Res_INTERNALID(rid)) {
             return rid;
         }
@@ -1584,7 +1641,8 @@ uint32_t ResourceTable::getResId(const String16& package,
 uint32_t ResourceTable::getResId(const String16& ref,
                                  const String16* defType,
                                  const String16* defPackage,
-                                 const char** outErrorMsg) const
+                                 const char** outErrorMsg,
+                                 bool onlyPublic) const
 {
     String16 package, type, name;
     if (!ResTable::expandResourceRef(
@@ -1603,7 +1661,7 @@ uint32_t ResourceTable::getResId(const String16& ref,
                      String8(name).string()));
         return 0;
     }
-    uint32_t res = getResId(package, type, name);
+    uint32_t res = getResId(package, type, name, onlyPublic);
     NOISY(printf("Expanded resource: p=%s, t=%s, n=%s, res=%d\n",
                  String8(package).string(), String8(type).string(),
                  String8(name).string(), res));
@@ -2036,6 +2094,93 @@ status_t ResourceTable::addSymbols(const sp<AaptSymbols>& outSymbols) {
 }
 
 
+void
+ResourceTable::addLocalization(const String16& name, const String8& locale)
+{
+    mLocalizations[name].insert(locale);
+}
+
+
+/*!
+ * Flag various sorts of localization problems.  '+' indicates checks already implemented;
+ * '-' indicates checks that will be implemented in the future.
+ *
+ * + A localized string for which no default-locale version exists => warning
+ * + A string for which no version in an explicitly-requested locale exists => warning
+ * + A localized translation of an translateable="false" string => warning
+ * - A localized string not provided in every locale used by the table
+ */
+status_t
+ResourceTable::validateLocalizations(void)
+{
+    status_t err = NO_ERROR;
+    const String8 defaultLocale;
+
+    // For all strings...
+    for (map<String16, set<String8> >::iterator nameIter = mLocalizations.begin();
+         nameIter != mLocalizations.end();
+         nameIter++) {
+        const set<String8>& configSet = nameIter->second;   // naming convenience
+
+        // 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->getResourceSourceDir());
+            for (set<String8>::iterator locales = configSet.begin();
+                 locales != configSet.end();
+                 locales++) {
+                fprintf(stdout, " %s", (*locales).string());
+            }
+            fprintf(stdout, "\n");
+            // !!! TODO: throw an error here in some circumstances
+        }
+
+        // Check that all requested localizations are present for this string
+        if (mBundle->getConfigurations() != NULL && mBundle->getRequireLocalization()) {
+            const char* allConfigs = mBundle->getConfigurations();
+            const char* start = allConfigs;
+            const char* comma;
+            
+            do {
+                String8 config;
+                comma = strchr(start, ',');
+                if (comma != NULL) {
+                    config.setTo(start, comma - start);
+                    start = comma + 1;
+                } else {
+                    config.setTo(start);
+                }
+
+                // don't bother with the pseudolocale "zz_ZZ"
+                if (config != "zz_ZZ") {
+                    if (configSet.find(config) == configSet.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()) {
+                            // TODO: force an error if there is no default to fall back to
+                            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->getResourceSourceDir());
+                                //err = UNKNOWN_ERROR;
+                            }
+                        }
+                    }
+                }
+           } while (comma != NULL);
+        }
+    }
+
+    return err;
+}
+
+
 status_t
 ResourceFilter::parse(const char* arg)
 {
@@ -2187,6 +2332,10 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
                 }
                 const size_t N = c->getEntries().size();
                 for (size_t ei=0; ei<N; ei++) {
+                    ConfigDescription config = c->getEntries().keyAt(ei);
+                    if (!filter.match(config)) {
+                        continue;
+                    }
                     sp<Entry> e = c->getEntries().valueAt(ei);
                     if (e == NULL) {
                         continue;
index b36234d37cc53b3317ad114dcd05fe68bfa13278..fff4f49795b0105361cde03322eab897dbfe68ec 100644 (file)
 #include "StringPool.h"
 #include "SourcePos.h"
 
+#include <set>
+#include <map>
+
+using namespace std;
+
 class ResourceTable;
 
 enum {
@@ -136,12 +141,14 @@ public:
 
     uint32_t getResId(const String16& package,
                       const String16& type,
-                      const String16& name) const;
+                      const String16& name,
+                      bool onlyPublic = false) const;
 
     uint32_t getResId(const String16& ref,
                       const String16* defType = NULL,
                       const String16* defPackage = NULL,
-                      const char** outErrorMsg = NULL) const;
+                      const char** outErrorMsg = NULL,
+                      bool onlyPublic = false) const;
 
     static bool isValidResourceName(const String16& s);
     
@@ -155,6 +162,8 @@ public:
 
     status_t assignResourceIds();
     status_t addSymbols(const sp<AaptSymbols>& outSymbols = NULL);
+    void addLocalization(const String16& name, const String8& locale);
+    status_t validateLocalizations(void);
 
     status_t flatten(Bundle*, const sp<AaptFile>& dest);
 
@@ -491,7 +500,6 @@ private:
 
     String16 mAssetsPackage;
     sp<AaptAssets> mAssets;
-    DefaultKeyedVector<String16, DefaultKeyedVector<String16, uint32_t> > mPublicNames;
     DefaultKeyedVector<String16, sp<Package> > mPackages;
     Vector<sp<Package> > mOrderedPackages;
     uint32_t mNextPackageId;
@@ -500,6 +508,9 @@ private:
     size_t mNumLocal;
     SourcePos mCurrentXmlPos;
     Bundle* mBundle;
+    
+    // key = string resource name, value = set of locales in which that name is defined
+    map<String16, set<String8> > mLocalizations;
 };
 
 class ResourceFilter
index 8f45959671077172a9f411950f9b097c4654fe8e..2ea453cbebe52d0d42beb7faa66dc150429ebeec 100644 (file)
@@ -21,6 +21,7 @@
 
 const char* const RESOURCES_ROOT_NAMESPACE = "http://schemas.android.com/apk/res/";
 const char* const RESOURCES_ANDROID_NAMESPACE = "http://schemas.android.com/apk/res/android";
+const char* const RESOURCES_ROOT_PRV_NAMESPACE = "http://schemas.android.com/apk/prv/res/";
 
 const char* const XLIFF_XMLNS = "urn:oasis:names:tc:xliff:document:1.2";
 const char* const ALLOWED_XLIFF_ELEMENTS[] = {
@@ -43,15 +44,27 @@ bool isWhitespace(const char16_t* str)
 }
 
 static const String16 RESOURCES_PREFIX(RESOURCES_ROOT_NAMESPACE);
+static const String16 RESOURCES_PRV_PREFIX(RESOURCES_ROOT_PRV_NAMESPACE);
 
-String16 getNamespaceResourcePackage(String16 namespaceUri)
+String16 getNamespaceResourcePackage(String16 namespaceUri, bool* outIsPublic)
 {
     //printf("%s starts with %s?\n", String8(namespaceUri).string(),
     //       String8(RESOURCES_PREFIX).string());
-    if (!namespaceUri.startsWith(RESOURCES_PREFIX)) return String16();
+    size_t prefixSize;
+    bool isPublic = true;
+    if (namespaceUri.startsWith(RESOURCES_PREFIX)) {
+        prefixSize = RESOURCES_PREFIX.size();
+    } else if (namespaceUri.startsWith(RESOURCES_PRV_PREFIX)) {
+        isPublic = false;
+        prefixSize = RESOURCES_PRV_PREFIX.size();
+    } else {
+        if (outIsPublic) *outIsPublic = isPublic; // = true
+        return String16();
+    }
+
     //printf("YES!\n");
-    const size_t prefixSize = RESOURCES_PREFIX.size();
     //printf("namespace: %s\n", String8(String16(namespaceUri, namespaceUri.size()-prefixSize, prefixSize)).string());
+    if (outIsPublic) *outIsPublic = isPublic;
     return String16(namespaceUri, namespaceUri.size()-prefixSize, prefixSize);
 }
 
@@ -715,15 +728,18 @@ status_t XMLNode::assignResourceIds(const sp<AaptAssets>& assets,
         for (size_t i=0; i<N; i++) {
             const attribute_entry& e = mAttributes.itemAt(i);
             if (e.ns.size() <= 0) continue;
-            String16 pkg(getNamespaceResourcePackage(e.ns));
-            NOISY(printf("Elem %s %s=\"%s\": namespace %s ===> %s\n",
+            bool nsIsPublic;
+            String16 pkg(getNamespaceResourcePackage(e.ns, &nsIsPublic));
+            NOISY(printf("Elem %s %s=\"%s\": namespace(%s) %s ===> %s\n",
                     String8(getElementName()).string(),
                     String8(e.name).string(),
                     String8(e.string).string(),
-                    String8(e.ns).string(), String8(pkg).string()));
+                    String8(e.ns).string(),
+                    (nsIsPublic) ? "public" : "private",
+                    String8(pkg).string()));
             if (pkg.size() <= 0) continue;
             uint32_t res = table != NULL
-                ? table->getResId(e.name, &attr, &pkg, &errorMsg)
+                ? table->getResId(e.name, &attr, &pkg, &errorMsg, nsIsPublic)
                 : assets->getIncludedResources().
                     identifierForName(e.name.string(), e.name.size(),
                                       attr.string(), attr.size(),
index 8c4243c53d2ee1bd98432a4a227043eabd703754..86548a22c85805f8819a1b9e1980a6fd8b1d22b9 100644 (file)
--- a/XMLNode.h
+++ b/XMLNode.h
@@ -17,7 +17,7 @@ extern const char* const RESOURCES_ANDROID_NAMESPACE;
 
 bool isWhitespace(const char16_t* str);
 
-String16 getNamespaceResourcePackage(String16 namespaceUri);
+String16 getNamespaceResourcePackage(String16 namespaceUri, bool* outIsPublic = NULL);
 
 status_t parseStyledString(Bundle* bundle,
                            const char* fileName,