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

Commit c9a2926e authored by Adam Lesinski's avatar Adam Lesinski
Browse files

AAPT2: Allow to inline XML into custom attribute

Previously, doing something like

<parent xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:aapt="http://schemas.android.com/aapt">
    <aapt:attr name="app:foo">
        <child />
    </aapt:attr>
</parent>

would result in something like:

<parent xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:aapt="http://schemas.android.com/aapt"
        foo="@generated_name" />

while it should result in:

<parent xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:aapt="http://schemas.android.com/aapt"
        app:foo="@generated_name" />

Bug: 36809755
Test: make aapt2_tests
Change-Id: I72ea4b402e196ca05b53b788e4768a265190a0dc
parent b437f30f
Loading
Loading
Loading
Loading
+9 −6
Original line number Diff line number Diff line
@@ -65,10 +65,6 @@ class Visitor : public xml::PackageAwareVisitor {
    }

    const ResourceName& name = ref.value().name.value();

    // Use an empty string for the compilation package because we don't want to default to
    // the local package if the user specified name="style" or something. This should just
    // be the default namespace.
    Maybe<xml::ExtractedPackage> maybe_pkg = TransformPackageAlias(name.package);
    if (!maybe_pkg) {
      context_->GetDiagnostics()->Error(DiagMessage(src)
@@ -83,9 +79,16 @@ class Visitor : public xml::PackageAwareVisitor {
    InlineDeclaration decl;
    decl.el = el;
    decl.attr_name = name.entry;
    if (!pkg.package.empty()) {

    // We need to differentiate between no-namespace defined, or the alias resolves to an empty
    // package, which means we must use the res-auto schema.
    if (!name.package.empty()) {
      if (pkg.package.empty()) {
        decl.attr_namespace_uri = xml::kSchemaAuto;
      } else {
        decl.attr_namespace_uri = xml::BuildPackageNamespace(pkg.package, private_namespace);
      }
    }

    inline_declarations_.push_back(std::move(decl));
  }
+38 −0
Original line number Diff line number Diff line
@@ -184,4 +184,42 @@ TEST(InlineXmlFormatParserTest, ExtractNestedXmlResources) {
  // Confirm that all of the nested inline xmls are parsed out.
  ASSERT_THAT(parser.GetExtractedInlineXmlDocuments(), SizeIs(8u));
}

TEST(InlineXmlFormatParserTest, ExtractIntoAppAttribute) {
  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(
      <parent xmlns:app="http://schemas.android.com/apk/res-auto"
              xmlns:aapt="http://schemas.android.com/aapt">
            <aapt:attr name="app:foo">
                <child />
            </aapt:attr>
      </parent>)");

  doc->file.name = test::ParseNameOrDie("layout/main");

  InlineXmlFormatParser parser;
  ASSERT_TRUE(parser.Consume(context.get(), doc.get()));

  ASSERT_THAT(doc->root, NotNull());
  EXPECT_THAT(doc->root->FindAttribute(xml::kSchemaAuto, "foo"), NotNull());
}

TEST(InlineXmlFormatParserTest, ExtractIntoNoNamespaceAttribute) {
  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(
      <parent xmlns:aapt="http://schemas.android.com/aapt">
            <aapt:attr name="foo">
                <child />
            </aapt:attr>
      </parent>)");

  doc->file.name = test::ParseNameOrDie("layout/main");

  InlineXmlFormatParser parser;
  ASSERT_TRUE(parser.Consume(context.get(), doc.get()));

  ASSERT_THAT(doc->root, NotNull());
  EXPECT_THAT(doc->root->FindAttribute({}, "foo"), NotNull());
}

}  // namespace aapt
+9 −1
Original line number Diff line number Diff line
@@ -154,6 +154,12 @@ class TestVisitor : public PackageAwareVisitor {
      EXPECT_THAT(TransformPackageAlias("two"), Eq(make_value(ExtractedPackage{"com.two", false})));
      EXPECT_THAT(TransformPackageAlias("three"),
                  Eq(make_value(ExtractedPackage{"com.three", false})));
    } else if (el->name == "View4") {
      EXPECT_THAT(TransformPackageAlias("one"), Eq(make_value(ExtractedPackage{"com.one", false})));
      EXPECT_THAT(TransformPackageAlias("two"), Eq(make_value(ExtractedPackage{"com.two", false})));
      EXPECT_THAT(TransformPackageAlias("three"),
                  Eq(make_value(ExtractedPackage{"com.three", false})));
      EXPECT_THAT(TransformPackageAlias("four"), Eq(make_value(ExtractedPackage{"", true})));
    }
  }
};
@@ -162,7 +168,9 @@ TEST(XmlDomTest, PackageAwareXmlVisitor) {
  std::unique_ptr<XmlResource> doc = test::BuildXmlDom(R"(
      <View1 xmlns:one="http://schemas.android.com/apk/res/com.one">
        <View2 xmlns:two="http://schemas.android.com/apk/res/com.two">
          <View3 xmlns:three="http://schemas.android.com/apk/res/com.three" />
          <View3 xmlns:three="http://schemas.android.com/apk/res/com.three">
            <View4 xmlns:four="http://schemas.android.com/apk/res-auto" />
          </View3>
        </View2>
      </View1>)");