Add @hide support for styleable attributes

AAPT2 generates documentation for styleables. The documentation contains
references to the attributes of the styleable. If the attributes are
marked @hide, remove the references to the attributes in the generated
coments.

Bug: 120262117
Test: m -j offline-sdk-docs
Change-Id: I541002077b17771d89caead04df2f4ae66c623f0
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index d1a70a7..31d205e 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -298,19 +298,20 @@
                          "<colgroup align=\"left\" />\n"
                          "<tr><th>Attribute</th><th>Description</th></tr>\n";
 
-    // Build the table of attributes with their links and names.
-    for (const StyleableAttr& entry : sorted_attributes) {
-      if (SkipSymbol(entry.symbol)) {
-        continue;
-      }
-
+    // Removed and hidden attributes are public but hidden from the documentation, so don't emit
+    // them as part of the class documentation.
+    std::vector<StyleableAttr> documentation_attrs = sorted_attributes;
+    auto documentation_remove_iter = std::remove_if(documentation_attrs.begin(),
+                                                    documentation_attrs.end(),
+                                                    [&](StyleableAttr entry) -> bool {
       StringPiece attr_comment_line = entry.symbol.value().attribute->GetComment();
-      if (attr_comment_line.contains("@removed")) {
-        // Removed attributes are public but hidden from the documentation, so
-        // don't emit them as part of the class documentation.
-        continue;
-      }
+      return SkipSymbol(entry.symbol) || attr_comment_line.contains("@removed")
+                                      || attr_comment_line.contains("@hide");
+    });
+    documentation_attrs.erase(documentation_remove_iter, documentation_attrs.end());
 
+    // Build the table of attributes with their links and names.
+    for (const StyleableAttr& entry : documentation_attrs) {
       const ResourceName& attr_name = entry.attr_ref->name.value();
       styleable_comment << "<tr><td><code>{@link #" << entry.field_name << " "
                         << (!attr_name.package.empty() ? attr_name.package
@@ -320,16 +321,14 @@
       // Only use the comment up until the first '.'. This is to stay compatible with
       // the way old AAPT did it (presumably to keep it short and to avoid including
       // annotations like @hide which would affect this Styleable).
+      StringPiece attr_comment_line = entry.symbol.value().attribute->GetComment();
       styleable_comment << "<td>" << AnnotationProcessor::ExtractFirstSentence(attr_comment_line)
                         << "</td></tr>\n";
     }
     styleable_comment << "</table>\n";
 
     // Generate the @see lines for each attribute.
-    for (const StyleableAttr& entry : sorted_attributes) {
-      if (SkipSymbol(entry.symbol)) {
-        continue;
-      }
+    for (const StyleableAttr& entry : documentation_attrs) {
       styleable_comment << "@see #" << entry.field_name << "\n";
     }
 
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index fa208be..4f51fc4 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -366,6 +366,46 @@
   ASSERT_TRUE(generator.Generate("android", &out));
   out.Flush();
 
+  EXPECT_THAT(output, HasSubstr("#Container_one android:one"));
+  EXPECT_THAT(output, HasSubstr("@see #Container_one"));
+  EXPECT_THAT(output, HasSubstr("attr name android:one"));
+  EXPECT_THAT(output, HasSubstr("attr description"));
+  EXPECT_THAT(output, HasSubstr(attr.GetComment()));
+  EXPECT_THAT(output, HasSubstr(styleable.GetComment()));
+}
+
+TEST(JavaClassGeneratorTest, CommentsForStyleableHiddenAttributesAreNotPresent) {
+  Attribute attr;
+  attr.SetComment(StringPiece("This is an attribute @hide"));
+
+  Styleable styleable;
+  styleable.entries.push_back(Reference(test::ParseNameOrDie("android:attr/one")));
+  styleable.SetComment(StringPiece("This is a styleable"));
+
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .SetPackageId("android", 0x01)
+          .AddValue("android:attr/one", util::make_unique<Attribute>(attr))
+          .AddValue("android:styleable/Container",
+                    std::unique_ptr<Styleable>(styleable.Clone(nullptr)))
+          .Build();
+
+  std::unique_ptr<IAaptContext> context =
+      test::ContextBuilder()
+          .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+          .SetNameManglerPolicy(NameManglerPolicy{"android"})
+          .Build();
+  JavaClassGeneratorOptions options;
+  options.use_final = false;
+  JavaClassGenerator generator(context.get(), table.get(), options);
+
+  std::string output;
+  StringOutputStream out(&output);
+  ASSERT_TRUE(generator.Generate("android", &out));
+  out.Flush();
+
+  EXPECT_THAT(output, Not(HasSubstr("#Container_one android:one")));
+  EXPECT_THAT(output, Not(HasSubstr("@see #Container_one")));
   EXPECT_THAT(output, HasSubstr("attr name android:one"));
   EXPECT_THAT(output, HasSubstr("attr description"));
   EXPECT_THAT(output, HasSubstr(attr.GetComment()));