Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5b7337f7 authored by Adam Lesinski's avatar Adam Lesinski
Browse files

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
parent 8276d0e1
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -1901,8 +1901,6 @@ int ResTable_config::compare(const ResTable_config& o) const {
    if (diff != 0) return diff;
    diff = (int32_t)(screenSize - o.screenSize);
    if (diff != 0) return diff;
    diff = (int32_t)(version - o.version);
    if (diff != 0) return diff;
    diff = (int32_t)(screenLayout - o.screenLayout);
    if (diff != 0) return diff;
    diff = (int32_t)(screenLayout2 - o.screenLayout2);
@@ -1914,6 +1912,11 @@ int ResTable_config::compare(const ResTable_config& o) const {
    diff = (int32_t)(smallestScreenWidthDp - o.smallestScreenWidthDp);
    if (diff != 0) return diff;
    diff = (int32_t)(screenSizeDp - o.screenSizeDp);
    if (diff != 0) return diff;

    // Version MUST be last to ensure that a sorted list of configurations will always have the
    // versions beside each other.
    diff = (int32_t)(version - o.version);
    return (int)diff;
}

+4 −0
Original line number Diff line number Diff line
@@ -987,4 +987,8 @@ bool ConfigDescription::IsCompatibleWith(const ConfigDescription& o) const {
  return !ConflictsWith(o) && !Dominates(o) && !o.Dominates(*this);
}

::std::ostream& operator<<(::std::ostream& out, const ConfigDescription& o) {
  return out << o.toString().string();
}

}  // namespace aapt
+34 −56
Original line number Diff line number Diff line
@@ -24,30 +24,22 @@

namespace aapt {

/*
 * Subclass of ResTable_config that adds convenient
 * initialization and comparison methods.
 */
// Subclass of ResTable_config that adds convenient
// initialization and comparison methods.
struct ConfigDescription : public android::ResTable_config {
  /**
   * Returns an immutable default config.
   */
  // Returns an immutable default config.
  static const ConfigDescription& DefaultConfig();

  /*
   * Parse a string of the form 'fr-sw600dp-land' and fill in the
   * given ResTable_config with resulting configuration parameters.
   *
   * The resulting configuration has the appropriate sdkVersion defined
   * for backwards compatibility.
   */
  // Parse a string of the form 'fr-sw600dp-land' and fill in the
  // given ResTable_config with resulting configuration parameters.
  //
  // The resulting configuration has the appropriate sdkVersion defined
  // for backwards compatibility.
  static bool Parse(const android::StringPiece& str, ConfigDescription* out = nullptr);

  /**
   * If the configuration uses an axis that was added after
   * the original Android release, make sure the SDK version
   * is set accordingly.
   */
  // If the configuration uses an axis that was added after
  // the original Android release, make sure the SDK version
  // is set accordingly.
  static void ApplyVersionForCompatibility(ConfigDescription* config);

  ConfigDescription();
@@ -61,38 +53,30 @@ struct ConfigDescription : public android::ResTable_config {

  ConfigDescription CopyWithoutSdkVersion() const;

  /**
   * A configuration X dominates another configuration Y, if X has at least the
   * precedence of Y and X is strictly more general than Y: for any type defined
   * by X, the same type is defined by Y with a value equal to or, in the case
   * of ranges, more specific than that of X.
   *
   * For example, the configuration 'en-w800dp' dominates 'en-rGB-w1024dp'. It
   * does not dominate 'fr', 'en-w720dp', or 'mcc001-en-w800dp'.
   */
  // A configuration X dominates another configuration Y, if X has at least the
  // precedence of Y and X is strictly more general than Y: for any type defined
  // by X, the same type is defined by Y with a value equal to or, in the case
  // of ranges, more specific than that of X.
  //
  // For example, the configuration 'en-w800dp' dominates 'en-rGB-w1024dp'. It
  // does not dominate 'fr', 'en-w720dp', or 'mcc001-en-w800dp'.
  bool Dominates(const ConfigDescription& o) const;

  /**
   * Returns true if this configuration defines a more important configuration
   * parameter than o. For example, "en" has higher precedence than "v23",
   * whereas "en" has the same precedence as "en-v23".
   */
  // Returns true if this configuration defines a more important configuration
  // parameter than o. For example, "en" has higher precedence than "v23",
  // whereas "en" has the same precedence as "en-v23".
  bool HasHigherPrecedenceThan(const ConfigDescription& o) const;

  /**
   * A configuration conflicts with another configuration if both
   * configurations define an incompatible configuration parameter. An
   * incompatible configuration parameter is a non-range, non-density parameter
   * that is defined in both configurations as a different, non-default value.
   */
  // A configuration conflicts with another configuration if both
  // configurations define an incompatible configuration parameter. An
  // incompatible configuration parameter is a non-range, non-density parameter
  // that is defined in both configurations as a different, non-default value.
  bool ConflictsWith(const ConfigDescription& o) const;

  /**
   * A configuration is compatible with another configuration if both
   * configurations can match a common concrete device configuration and are
   * unrelated by domination. For example, land-v11 conflicts with port-v21
   * but is compatible with v21 (both land-v11 and v21 would match en-land-v23).
   */
  // A configuration is compatible with another configuration if both
  // configurations can match a common concrete device configuration and are
  // unrelated by domination. For example, land-v11 conflicts with port-v21
  // but is compatible with v21 (both land-v11 and v21 would match en-land-v23).
  bool IsCompatibleWith(const ConfigDescription& o) const;

  bool MatchWithDensity(const ConfigDescription& o) const;
@@ -105,6 +89,8 @@ struct ConfigDescription : public android::ResTable_config {
  bool operator>(const ConfigDescription& o) const;
};

::std::ostream& operator<<(::std::ostream& out, const ConfigDescription& o);

inline ConfigDescription::ConfigDescription() {
  memset(this, 0, sizeof(*this));
  size = sizeof(android::ResTable_config);
@@ -123,15 +109,13 @@ inline ConfigDescription::ConfigDescription(ConfigDescription&& o) {
  *this = o;
}

inline ConfigDescription& ConfigDescription::operator=(
    const android::ResTable_config& o) {
inline ConfigDescription& ConfigDescription::operator=(const android::ResTable_config& o) {
  *static_cast<android::ResTable_config*>(this) = o;
  size = sizeof(android::ResTable_config);
  return *this;
}

inline ConfigDescription& ConfigDescription::operator=(
    const ConfigDescription& o) {
inline ConfigDescription& ConfigDescription::operator=(const ConfigDescription& o) {
  *static_cast<android::ResTable_config*>(this) = o;
  return *this;
}
@@ -141,8 +125,7 @@ inline ConfigDescription& ConfigDescription::operator=(ConfigDescription&& o) {
  return *this;
}

inline bool ConfigDescription::MatchWithDensity(
    const ConfigDescription& o) const {
inline bool ConfigDescription::MatchWithDensity(const ConfigDescription& o) const {
  return match(o) && (density == 0 || density == o.density);
}

@@ -170,11 +153,6 @@ inline bool ConfigDescription::operator>(const ConfigDescription& o) const {
  return compare(o) > 0;
}

inline ::std::ostream& operator<<(::std::ostream& out,
                                  const ConfigDescription& o) {
  return out << o.toString().string();
}

}  // namespace aapt

#endif  // AAPT_CONFIG_DESCRIPTION_H
+57 −29
Original line number Diff line number Diff line
@@ -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 @@ class ResourceFileFlattener {
    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 @@ uint32_t ResourceFileFlattener::GetCompressionFlags(const StringPiece& str) {
  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 @@ std::vector<std::unique_ptr<xml::XmlResource>> ResourceFileFlattener::LinkAndVer
    }
  }

  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 ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv
  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 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv
            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
+20 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->

<adaptive-icon xmlns:android="http://schema.android.com/apk/res/android">
    <background android:drawable="@android:color/white" />
    <foreground android:drawable="@drawable/image" />
</adaptive-icon>
Loading