AAPT2: Auto-version adaptive-icon XML
Auto version adaptive-icon XML to v26.
This change makes the logic for generating versioned resources
simpler by changing the comparison function of ResTable_config
to evaluate the sdkVersion property last, making configurations
that differ only in sdkVersion next to each other in a sorted vector.
Bug: 62316340
Test: manual (verified output of tools/aapt2/integration-tests/AppOne)
Change-Id: I977d45821722a65d2135efb4693304eacc565c9a
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index e6bf3a6..353d734 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -60,6 +60,7 @@
#include "unflatten/BinaryResourceParser.h"
#include "util/Files.h"
#include "xml/XmlDom.h"
+#include "xml/XmlUtil.h"
using android::StringPiece;
using android::base::StringPrintf;
@@ -342,16 +343,18 @@
ConfigDescription config;
// The entry this file came from.
- ResourceEntry* entry;
+ ResourceEntry* entry = nullptr;
// The file to copy as-is.
- io::IFile* file_to_copy;
+ io::IFile* file_to_copy = nullptr;
// The XML to process and flatten.
std::unique_ptr<xml::XmlResource> xml_to_flatten;
// The destination to write this file to.
std::string dst_path;
+
+ bool skip_versioning = false;
};
uint32_t GetCompressionFlags(const StringPiece& str);
@@ -431,19 +434,6 @@
return ArchiveEntry::kCompress;
}
-static bool IsTransitionElement(const std::string& name) {
- return name == "fade" || name == "changeBounds" || name == "slide" || name == "explode" ||
- name == "changeImageTransform" || name == "changeTransform" ||
- name == "changeClipBounds" || name == "autoTransition" || name == "recolor" ||
- name == "changeScroll" || name == "transitionSet" || name == "transition" ||
- name == "transitionManager";
-}
-
-static bool IsVectorElement(const std::string& name) {
- return name == "vector" || name == "animated-vector" || name == "pathInterpolator" ||
- name == "objectAnimator";
-}
-
template <typename T>
std::vector<T> make_singleton_vec(T&& val) {
std::vector<T> vec;
@@ -476,21 +466,10 @@
}
}
- if (options_.no_auto_version) {
+ if (options_.no_auto_version || file_op->skip_versioning) {
return make_singleton_vec(std::move(file_op->xml_to_flatten));
}
- if (options_.no_version_vectors || options_.no_version_transitions) {
- // Skip this if it is a vector or animated-vector.
- xml::Element* el = xml::FindRootElement(doc);
- if (el && el->namespace_uri.empty()) {
- if ((options_.no_version_vectors && IsVectorElement(el->name)) ||
- (options_.no_version_transitions && IsTransitionElement(el->name))) {
- return make_singleton_vec(std::move(file_op->xml_to_flatten));
- }
- }
- }
-
const ConfigDescription& config = file_op->config;
ResourceEntry* entry = file_op->entry;
@@ -504,15 +483,26 @@
bool error = false;
std::map<std::pair<ConfigDescription, StringPiece>, FileOperation> config_sorted_files;
+ int tag_version_options = 0;
+ if (options_.no_version_vectors) {
+ tag_version_options |= xml::kNoVersionVector;
+ }
+
+ if (options_.no_version_transitions) {
+ tag_version_options |= xml::kNoVersionTransition;
+ }
+
for (auto& pkg : table->packages) {
for (auto& type : pkg->types) {
// Sort by config and name, so that we get better locality in the zip file.
config_sorted_files.clear();
- std::queue<FileOperation> file_operations;
// Populate the queue with all files in the ResourceTable.
for (auto& entry : type->entries) {
- for (auto& config_value : entry->values) {
+ const auto values_end = entry->values.end();
+ for (auto values_iter = entry->values.begin(); values_iter != values_end; ++values_iter) {
+ ResourceConfigValue* config_value = values_iter->get();
+
// WARNING! Do not insert or remove any resources while executing in this scope. It will
// corrupt the iteration order.
@@ -554,6 +544,44 @@
file_op.xml_to_flatten->file.config = config_value->config;
file_op.xml_to_flatten->file.source = file_ref->GetSource();
file_op.xml_to_flatten->file.name = ResourceName(pkg->name, type->type, entry->name);
+
+ // Check if this file needs to be versioned based on tag rules.
+ xml::Element* root_el = xml::FindRootElement(file_op.xml_to_flatten.get());
+ if (root_el == nullptr) {
+ context_->GetDiagnostics()->Error(DiagMessage(file->GetSource())
+ << "failed to find the root XML element");
+ return false;
+ }
+
+ if (root_el->namespace_uri.empty()) {
+ if (Maybe<xml::TagApiVersionResult> result =
+ xml::GetXmlTagApiVersion(root_el->name, tag_version_options)) {
+ file_op.skip_versioning = result.value().skip_version;
+ if (result.value().api_version && config_value->config.sdkVersion == 0u) {
+ const ApiVersion min_tag_version = result.value().api_version.value();
+ // Only version it if it doesn't specify its own version and the version is
+ // greater than the minSdk.
+ const util::Range<ApiVersion> valid_range{
+ context_->GetMinSdkVersion() + 1,
+ FindNextApiVersionForConfigInSortedVector(values_iter, values_end)};
+ if (valid_range.Contains(min_tag_version)) {
+ // Update the configurations. The iteration order will not be affected
+ // since sdkVersions in ConfigDescriptions are the last property compared
+ // in the sort function.
+ if (context_->IsVerbose()) {
+ context_->GetDiagnostics()->Note(DiagMessage(config_value->value->GetSource())
+ << "auto-versioning XML resource to API "
+ << min_tag_version);
+ }
+ const_cast<ConfigDescription&>(config_value->config).sdkVersion =
+ static_cast<uint16_t>(min_tag_version);
+ file_op.config.sdkVersion = static_cast<uint16_t>(min_tag_version);
+ file_op.xml_to_flatten->file.config.sdkVersion =
+ static_cast<uint16_t>(min_tag_version);
+ }
+ }
+ }
+ }
}
// NOTE(adamlesinski): Explicitly construct a StringPiece here, or