summaryrefslogtreecommitdiff
path: root/tools/aapt/Resource.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/aapt/Resource.cpp')
-rw-r--r--tools/aapt/Resource.cpp312
1 files changed, 183 insertions, 129 deletions
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 42b190572cbd..3d93bbe62f67 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -166,6 +166,35 @@ private:
ResTable_config mParams;
};
+class AnnotationProcessor {
+public:
+ AnnotationProcessor() : mDeprecated(false), mSystemApi(false) { }
+
+ void preprocessComment(String8& comment) {
+ if (comment.size() > 0) {
+ if (comment.contains("@deprecated")) {
+ mDeprecated = true;
+ }
+ if (comment.removeAll("@SystemApi")) {
+ mSystemApi = true;
+ }
+ }
+ }
+
+ void printAnnotations(FILE* fp, const char* indentStr) {
+ if (mDeprecated) {
+ fprintf(fp, "%s@Deprecated\n", indentStr);
+ }
+ if (mSystemApi) {
+ fprintf(fp, "%s@android.annotation.SystemApi\n", indentStr);
+ }
+ }
+
+private:
+ bool mDeprecated;
+ bool mSystemApi;
+};
+
// ==========================================================================
// ==========================================================================
// ==========================================================================
@@ -179,24 +208,6 @@ bool isValidResourceType(const String8& type)
|| type == "color" || type == "menu" || type == "mipmap";
}
-static sp<AaptFile> getResourceFile(const sp<AaptAssets>& assets, bool makeIfNecessary=true)
-{
- sp<AaptGroup> group = assets->getFiles().valueFor(String8("resources.arsc"));
- sp<AaptFile> file;
- if (group != NULL) {
- file = group->getFiles().valueFor(AaptGroupEntry());
- if (file != NULL) {
- return file;
- }
- }
-
- if (!makeIfNecessary) {
- return NULL;
- }
- return assets->addFile(String8("resources.arsc"), AaptGroupEntry(), String8(),
- NULL, String8());
-}
-
static status_t parsePackage(Bundle* bundle, const sp<AaptAssets>& assets,
const sp<AaptGroup>& grp)
{
@@ -359,23 +370,6 @@ static status_t preProcessImages(const Bundle* bundle, const sp<AaptAssets>& ass
return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR;
}
-status_t postProcessImages(const sp<AaptAssets>& assets,
- ResourceTable* table,
- const sp<ResourceTypeSet>& set)
-{
- ResourceDirIterator it(set, String8("drawable"));
- bool hasErrors = false;
- ssize_t res;
- while ((res=it.next()) == NO_ERROR) {
- res = postProcessImage(assets, table, it.getFile());
- if (res < NO_ERROR) {
- hasErrors = true;
- }
- }
-
- return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR;
-}
-
static void collect_files(const sp<AaptDir>& dir,
KeyedVector<String8, sp<ResourceTypeSet> >* resources)
{
@@ -470,7 +464,7 @@ static int validateAttr(const String8& path, const ResTable& table,
value.data);
return ATTR_NOT_FOUND;
}
-
+
pool = table.getTableStringBlock(strIdx);
#if 0
if (pool != NULL) {
@@ -595,11 +589,11 @@ static bool applyFileOverlay(Bundle *bundle,
if (bundle->getVerbose()) {
printf("trying overlaySet Key=%s\n",overlaySet->keyAt(overlayIndex).string());
}
- size_t baseIndex = UNKNOWN_ERROR;
+ ssize_t baseIndex = -1;
if (baseSet->get() != NULL) {
baseIndex = (*baseSet)->indexOfKey(overlaySet->keyAt(overlayIndex));
}
- if (baseIndex < UNKNOWN_ERROR) {
+ if (baseIndex >= 0) {
// look for same flavor. For a given file (strings.xml, for example)
// there may be a locale specific or other flavors - we want to match
// the same flavor.
@@ -625,10 +619,10 @@ static bool applyFileOverlay(Bundle *bundle,
for (size_t overlayGroupIndex = 0;
overlayGroupIndex<overlayGroupSize;
overlayGroupIndex++) {
- size_t baseFileIndex =
+ ssize_t baseFileIndex =
baseGroup->getFiles().indexOfKey(overlayFiles.
keyAt(overlayGroupIndex));
- if (baseFileIndex < UNKNOWN_ERROR) {
+ if (baseFileIndex >= 0) {
if (bundle->getVerbose()) {
printf("found a match (" ZD ") for overlay file %s, for flavor %s\n",
(ZD_TYPE) baseFileIndex,
@@ -676,13 +670,15 @@ static bool applyFileOverlay(Bundle *bundle,
}
/*
- * Inserts an attribute in a given node, only if the attribute does not
- * exist.
+ * Inserts an attribute in a given node.
* If errorOnFailedInsert is true, and the attribute already exists, returns false.
- * Returns true otherwise, even if the attribute already exists.
+ * If replaceExisting is true, the attribute will be updated if it already exists.
+ * Returns true otherwise, even if the attribute already exists, and does not modify
+ * the existing attribute's value.
*/
bool addTagAttribute(const sp<XMLNode>& node, const char* ns8,
- const char* attr8, const char* value, bool errorOnFailedInsert)
+ const char* attr8, const char* value, bool errorOnFailedInsert,
+ bool replaceExisting)
{
if (value == NULL) {
return true;
@@ -691,7 +687,16 @@ bool addTagAttribute(const sp<XMLNode>& node, const char* ns8,
const String16 ns(ns8);
const String16 attr(attr8);
- if (node->getAttribute(ns, attr) != NULL) {
+ XMLNode::attribute_entry* existingEntry = node->editAttribute(ns, attr);
+ if (existingEntry != NULL) {
+ if (replaceExisting) {
+ NOISY(printf("Info: AndroidManifest.xml already defines %s (in %s);"
+ " overwriting existing value from manifest.\n",
+ String8(attr).string(), String8(ns).string()));
+ existingEntry->string = String16(value);
+ return true;
+ }
+
if (errorOnFailedInsert) {
fprintf(stderr, "Error: AndroidManifest.xml already defines %s (in %s);"
" cannot insert new value %s.\n",
@@ -706,11 +711,23 @@ bool addTagAttribute(const sp<XMLNode>& node, const char* ns8,
// don't stop the build.
return true;
}
-
+
node->addAttribute(ns, attr, String16(value));
return true;
}
+/*
+ * Inserts an attribute in a given node, only if the attribute does not
+ * exist.
+ * If errorOnFailedInsert is true, and the attribute already exists, returns false.
+ * Returns true otherwise, even if the attribute already exists.
+ */
+bool addTagAttribute(const sp<XMLNode>& node, const char* ns8,
+ const char* attr8, const char* value, bool errorOnFailedInsert)
+{
+ return addTagAttribute(node, ns8, attr8, value, errorOnFailedInsert, false);
+}
+
static void fullyQualifyClassName(const String8& package, sp<XMLNode> node,
const String16& attrName) {
XMLNode::attribute_entry* attr = node->editAttribute(
@@ -748,16 +765,17 @@ status_t massageManifest(Bundle* bundle, sp<XMLNode> root)
}
bool errorOnFailedInsert = bundle->getErrorOnFailedInsert();
+ bool replaceVersion = bundle->getReplaceVersion();
if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionCode",
- bundle->getVersionCode(), errorOnFailedInsert)) {
+ bundle->getVersionCode(), errorOnFailedInsert, replaceVersion)) {
return UNKNOWN_ERROR;
}
if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionName",
- bundle->getVersionName(), errorOnFailedInsert)) {
+ bundle->getVersionName(), errorOnFailedInsert, replaceVersion)) {
return UNKNOWN_ERROR;
}
-
+
if (bundle->getMinSdkVersion() != NULL
|| bundle->getTargetSdkVersion() != NULL
|| bundle->getMaxSdkVersion() != NULL) {
@@ -766,7 +784,7 @@ status_t massageManifest(Bundle* bundle, sp<XMLNode> root)
vers = XMLNode::newElement(root->getFilename(), String16(), String16("uses-sdk"));
root->insertChildAt(vers, 0);
}
-
+
if (!addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "minSdkVersion",
bundle->getMinSdkVersion(), errorOnFailedInsert)) {
return UNKNOWN_ERROR;
@@ -841,7 +859,7 @@ status_t massageManifest(Bundle* bundle, sp<XMLNode> root)
}
}
}
-
+
return NO_ERROR;
}
@@ -882,7 +900,38 @@ status_t updatePreProcessedCache(Bundle* bundle)
return 0;
}
-status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
+status_t generateAndroidManifestForSplit(const String16& package, const sp<ApkSplit>& split,
+ sp<AaptFile>& outFile) {
+ const String8 filename("AndroidManifest.xml");
+ const String16 androidPrefix("android");
+ const String16 androidNSUri("http://schemas.android.com/apk/res/android");
+ sp<XMLNode> root = XMLNode::newNamespace(filename, androidPrefix, androidNSUri);
+
+ // Build the <manifest> tag
+ sp<XMLNode> manifest = XMLNode::newElement(filename, String16(), String16("manifest"));
+
+ // Add the 'package' attribute which is set to the original package name.
+ manifest->addAttribute(String16(), String16("package"), package);
+
+ // Add the 'split' attribute which describes the configurations included.
+ String8 splitName("config_");
+ splitName.append(split->getDirectorySafeName());
+ manifest->addAttribute(String16(), String16("split"), String16(splitName));
+
+ // Build an empty <application> tag (required).
+ sp<XMLNode> app = XMLNode::newElement(filename, String16(), String16("application"));
+ manifest->addChild(app);
+ root->addChild(manifest);
+
+ status_t err = root->flatten(outFile, true, true);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ outFile->setCompressionMethod(ZipEntry::kCompressDeflated);
+ return NO_ERROR;
+}
+
+status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuilder>& builder)
{
// First, look for a package file to parse. This is required to
// be able to generate the resource information.
@@ -925,7 +974,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
// --------------------------------------------------------------
// resType -> leafName -> group
- KeyedVector<String8, sp<ResourceTypeSet> > *resources =
+ KeyedVector<String8, sp<ResourceTypeSet> > *resources =
new KeyedVector<String8, sp<ResourceTypeSet> >;
collect_files(assets, resources);
@@ -957,7 +1006,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
// now go through any resource overlays and collect their files
sp<AaptAssets> current = assets->getOverlay();
while(current.get()) {
- KeyedVector<String8, sp<ResourceTypeSet> > *resources =
+ KeyedVector<String8, sp<ResourceTypeSet> > *resources =
new KeyedVector<String8, sp<ResourceTypeSet> >;
current->setResources(resources);
collect_files(current, resources);
@@ -1060,7 +1109,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
// compile resources
current = assets;
while(current.get()) {
- KeyedVector<String8, sp<ResourceTypeSet> > *resources =
+ KeyedVector<String8, sp<ResourceTypeSet> > *resources =
current->getResources();
ssize_t index = resources->indexOfKey(String8("values"));
@@ -1069,7 +1118,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
ssize_t res;
while ((res=it.next()) == NO_ERROR) {
sp<AaptFile> file = it.getFile();
- res = compileResourceFile(bundle, assets, file, it.getParams(),
+ res = compileResourceFile(bundle, assets, file, it.getParams(),
(current!=assets), &table);
if (res != NO_ERROR) {
hasErrors = true;
@@ -1098,12 +1147,6 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
// --------------------------------------------------------------------
if (table.hasResources()) {
- sp<AaptFile> resFile(getResourceFile(assets));
- if (resFile == NULL) {
- fprintf(stderr, "Error: unable to generate entry for resource data\n");
- return UNKNOWN_ERROR;
- }
-
err = table.assignResourceIds();
if (err < NO_ERROR) {
return err;
@@ -1211,16 +1254,24 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
}
if (drawables != NULL) {
- err = postProcessImages(assets, &table, drawables);
- if (err != NO_ERROR) {
+ ResourceDirIterator it(drawables, String8("drawable"));
+ while ((err=it.next()) == NO_ERROR) {
+ err = postProcessImage(assets, &table, it.getFile());
+ if (err != NO_ERROR) {
+ hasErrors = true;
+ }
+ }
+
+ if (err < NO_ERROR) {
hasErrors = true;
}
+ err = NO_ERROR;
}
if (colors != NULL) {
ResourceDirIterator it(colors, String8("color"));
while ((err=it.next()) == NO_ERROR) {
- err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
+ err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
if (err != NO_ERROR) {
hasErrors = true;
}
@@ -1255,7 +1306,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
if (table.validateLocalizations()) {
hasErrors = true;
}
-
+
if (hasErrors) {
return UNKNOWN_ERROR;
}
@@ -1288,7 +1339,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
ResTable finalResTable;
sp<AaptFile> resFile;
-
+
if (table.hasResources()) {
sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R"));
err = table.addSymbols(symbols);
@@ -1296,15 +1347,39 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
return err;
}
- resFile = getResourceFile(assets);
- if (resFile == NULL) {
- fprintf(stderr, "Error: unable to generate entry for resource data\n");
- return UNKNOWN_ERROR;
- }
+ Vector<sp<ApkSplit> >& splits = builder->getSplits();
+ const size_t numSplits = splits.size();
+ for (size_t i = 0; i < numSplits; i++) {
+ sp<ApkSplit>& split = splits.editItemAt(i);
+ sp<AaptFile> flattenedTable = new AaptFile(String8("resources.arsc"),
+ AaptGroupEntry(), String8());
+ err = table.flatten(bundle, split->getResourceFilter(), flattenedTable);
+ if (err != NO_ERROR) {
+ fprintf(stderr, "Failed to generate resource table for split '%s'\n",
+ split->getPrintableName().string());
+ return err;
+ }
+ split->addEntry(String8("resources.arsc"), flattenedTable);
- err = table.flatten(bundle, resFile);
- if (err < NO_ERROR) {
- return err;
+ if (split->isBase()) {
+ resFile = flattenedTable;
+ err = finalResTable.add(flattenedTable->getData(), flattenedTable->getSize());
+ if (err != NO_ERROR) {
+ fprintf(stderr, "Generated resource table is corrupt.\n");
+ return err;
+ }
+ } else {
+ sp<AaptFile> generatedManifest = new AaptFile(String8("AndroidManifest.xml"),
+ AaptGroupEntry(), String8());
+ err = generateAndroidManifestForSplit(String16(assets->getPackage()), split,
+ generatedManifest);
+ if (err != NO_ERROR) {
+ fprintf(stderr, "Failed to generate AndroidManifest.xml for split '%s'\n",
+ split->getPrintableName().string());
+ return err;
+ }
+ split->addEntry(String8("AndroidManifest.xml"), generatedManifest);
+ }
}
if (bundle->getPublicOutputFile()) {
@@ -1321,14 +1396,10 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
fclose(fp);
}
- // Read resources back in,
- finalResTable.add(resFile->getData(), resFile->getSize());
-#if 0
- NOISY(
- printf("Generated resources:\n");
- finalResTable.print();
- )
-#endif
+ if (finalResTable.getTableCount() == 0 || resFile == NULL) {
+ fprintf(stderr, "No resource table was generated.\n");
+ return UNKNOWN_ERROR;
+ }
}
// Perform a basic validation of the manifest file. This time we
@@ -1425,7 +1496,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
ssize_t index = block.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "name");
const uint16_t* id = block.getAttributeStringValue(index, &len);
if (id == NULL) {
- fprintf(stderr, "%s:%d: missing name attribute in element <%s>.\n",
+ fprintf(stderr, "%s:%d: missing name attribute in element <%s>.\n",
manifestPath.string(), block.getLineNumber(),
String8(block.getElementName(&len)).string());
hasErrors = true;
@@ -1583,7 +1654,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
return err;
}
}
-
+
return err;
}
@@ -1704,16 +1775,13 @@ static status_t writeLayoutClasses(
NA = idents.size();
- bool deprecated = false;
-
String16 comment = symbols->getComment(realClassName);
+ AnnotationProcessor ann;
fprintf(fp, "%s/** ", indentStr);
if (comment.size() > 0) {
String8 cmt(comment);
+ ann.preprocessComment(cmt);
fprintf(fp, "%s\n", cmt.string());
- if (strstr(cmt.string(), "@deprecated") != NULL) {
- deprecated = true;
- }
} else {
fprintf(fp, "Attributes that can be used with a %s.\n", nclassName.string());
}
@@ -1785,10 +1853,8 @@ static status_t writeLayoutClasses(
}
fprintf(fp, "%s */\n", getIndentSpace(indent));
- if (deprecated) {
- fprintf(fp, "%s@Deprecated\n", indentStr);
- }
-
+ ann.printAnnotations(fp, indentStr);
+
fprintf(fp,
"%spublic static final int[] %s = {\n"
"%s",
@@ -1834,16 +1900,13 @@ static status_t writeLayoutClasses(
// String8(attr16).string(), String8(name16).string(), typeSpecFlags);
const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
- bool deprecated = false;
-
+ AnnotationProcessor ann;
fprintf(fp, "%s/**\n", indentStr);
if (comment.size() > 0) {
String8 cmt(comment);
+ ann.preprocessComment(cmt);
fprintf(fp, "%s <p>\n%s @attr description\n", indentStr, indentStr);
fprintf(fp, "%s %s\n", indentStr, cmt.string());
- if (strstr(cmt.string(), "@deprecated") != NULL) {
- deprecated = true;
- }
} else {
fprintf(fp,
"%s <p>This symbol is the offset where the {@link %s.R.attr#%s}\n"
@@ -1855,10 +1918,8 @@ static status_t writeLayoutClasses(
}
if (typeComment.size() > 0) {
String8 cmt(typeComment);
+ ann.preprocessComment(cmt);
fprintf(fp, "\n\n%s %s\n", indentStr, cmt.string());
- if (strstr(cmt.string(), "@deprecated") != NULL) {
- deprecated = true;
- }
}
if (comment.size() > 0) {
if (pub) {
@@ -1877,9 +1938,7 @@ static status_t writeLayoutClasses(
getSymbolPackage(name8, assets, pub).string(),
getSymbolName(name8).string());
fprintf(fp, "%s*/\n", indentStr);
- if (deprecated) {
- fprintf(fp, "%s@Deprecated\n", indentStr);
- }
+ ann.printAnnotations(fp, indentStr);
fprintf(fp,
"%spublic static final int %s_%s = %d;\n",
indentStr, nclassName.string(),
@@ -2018,16 +2077,14 @@ static status_t writeSymbolClass(
String8 name8(sym.name);
String16 comment(sym.comment);
bool haveComment = false;
- bool deprecated = false;
+ AnnotationProcessor ann;
if (comment.size() > 0) {
haveComment = true;
String8 cmt(comment);
+ ann.preprocessComment(cmt);
fprintf(fp,
"%s/** %s\n",
getIndentSpace(indent), cmt.string());
- if (strstr(cmt.string(), "@deprecated") != NULL) {
- deprecated = true;
- }
} else if (sym.isPublic && !includePrivate) {
sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
assets->getPackage().string(), className.string(),
@@ -2036,6 +2093,7 @@ static status_t writeSymbolClass(
String16 typeComment(sym.typeComment);
if (typeComment.size() > 0) {
String8 cmt(typeComment);
+ ann.preprocessComment(cmt);
if (!haveComment) {
haveComment = true;
fprintf(fp,
@@ -2044,16 +2102,11 @@ static status_t writeSymbolClass(
fprintf(fp,
"%s %s\n", getIndentSpace(indent), cmt.string());
}
- if (strstr(cmt.string(), "@deprecated") != NULL) {
- deprecated = true;
- }
}
if (haveComment) {
fprintf(fp,"%s */\n", getIndentSpace(indent));
}
- if (deprecated) {
- fprintf(fp, "%s@Deprecated\n", getIndentSpace(indent));
- }
+ ann.printAnnotations(fp, getIndentSpace(indent));
fprintf(fp, id_format,
getIndentSpace(indent),
flattenSymbol(name8).string(), (int)sym.int32Val);
@@ -2069,25 +2122,21 @@ static status_t writeSymbolClass(
}
String8 name8(sym.name);
String16 comment(sym.comment);
- bool deprecated = false;
+ AnnotationProcessor ann;
if (comment.size() > 0) {
String8 cmt(comment);
+ ann.preprocessComment(cmt);
fprintf(fp,
"%s/** %s\n"
"%s */\n",
getIndentSpace(indent), cmt.string(),
getIndentSpace(indent));
- if (strstr(cmt.string(), "@deprecated") != NULL) {
- deprecated = true;
- }
} else if (sym.isPublic && !includePrivate) {
sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
assets->getPackage().string(), className.string(),
String8(sym.name).string());
}
- if (deprecated) {
- fprintf(fp, "%s@Deprecated\n", getIndentSpace(indent));
- }
+ ann.printAnnotations(fp, getIndentSpace(indent));
fprintf(fp, "%spublic static final String %s=\"%s\";\n",
getIndentSpace(indent),
flattenSymbol(name8).string(), sym.stringVal.string());
@@ -2438,7 +2487,7 @@ struct NamespaceAttributePair {
status_t
writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile,
- const char* startTag, const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs)
+ const Vector<String8>& startTags, const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs)
{
status_t err;
ResXMLTree tree;
@@ -2452,15 +2501,18 @@ writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile,
tree.restart();
- if (startTag != NULL) {
+ if (!startTags.isEmpty()) {
bool haveStart = false;
while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
if (code != ResXMLTree::START_TAG) {
continue;
}
String8 tag(tree.getElementName(&len));
- if (tag == startTag) {
- haveStart = true;
+ const size_t numStartTags = startTags.size();
+ for (size_t i = 0; i < numStartTags; i++) {
+ if (tag == startTags[i]) {
+ haveStart = true;
+ }
}
break;
}
@@ -2547,15 +2599,17 @@ writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
for (size_t k=0; k<K; k++) {
const sp<AaptDir>& d = dirs.itemAt(k);
const String8& dirName = d->getLeaf();
+ Vector<String8> startTags;
const char* startTag = NULL;
const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs = NULL;
if ((dirName == String8("layout")) || (strncmp(dirName.string(), "layout-", 7) == 0)) {
tagAttrPairs = &kLayoutTagAttrPairs;
} else if ((dirName == String8("xml")) || (strncmp(dirName.string(), "xml-", 4) == 0)) {
- startTag = "PreferenceScreen";
+ startTags.add(String8("PreferenceScreen"));
+ startTags.add(String8("preference-headers"));
tagAttrPairs = &kXmlTagAttrPairs;
} else if ((dirName == String8("menu")) || (strncmp(dirName.string(), "menu-", 5) == 0)) {
- startTag = "menu";
+ startTags.add(String8("menu"));
tagAttrPairs = NULL;
} else {
continue;
@@ -2568,7 +2622,7 @@ writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files = group->getFiles();
const size_t M = files.size();
for (size_t j=0; j<M; j++) {
- err = writeProguardForXml(keep, files.valueAt(j), startTag, tagAttrPairs);
+ err = writeProguardForXml(keep, files.valueAt(j), startTags, tagAttrPairs);
if (err < 0) {
return err;
}