Loading core/tests/resourceflaggingtests/src/com/android/resourceflaggingtests/ResourceFlaggingTest.java +39 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import android.util.DisplayMetrics; import android.view.LayoutInflater; import android.view.View; import android.widget.LinearLayout; import android.widget.TextView; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; Loading Loading @@ -103,6 +104,26 @@ public class ResourceFlaggingTest { assertThat(mResources.getIntArray(R.array.intarr1)).isEqualTo(new int[]{1, 2, 3}); } @Test public void testDirectoryEnabledFlag() { assertThat(mResources.getBoolean(R.bool.bool8)).isTrue(); } @Test public void testDirectoryDisabledFlag() { assertThat(mResources.getBoolean(R.bool.bool7)).isTrue(); } @Test public void testDirectoryNegatedEnabledFlag() { assertThat(mResources.getBoolean(R.bool.bool9)).isTrue(); } @Test public void testDirectoryNegatedDisabledFlag() { assertThat(mResources.getBoolean(R.bool.bool10)).isTrue(); } @Test public void testLayoutWithDisabledElements() { LinearLayout ll = (LinearLayout) getLayoutInflater().inflate(R.layout.layout1, null); Loading @@ -112,6 +133,24 @@ public class ResourceFlaggingTest { assertThat((View) ll.findViewById(R.id.text2)).isNotNull(); } @Test public void testEnabledFlagLayoutOverrides() { LinearLayout ll = (LinearLayout) getLayoutInflater().inflate(R.layout.layout3, null); assertThat(ll).isNotNull(); assertThat((View) ll.findViewById(R.id.text1)).isNotNull(); assertThat(((TextView) ll.findViewById(R.id.text1)).getText()).isEqualTo("foobar"); } @Test(expected = Resources.NotFoundException.class) public void testDisabledLayout() { getLayoutInflater().inflate(R.layout.layout2, null); } @Test(expected = Resources.NotFoundException.class) public void testDisabledDrawable() { mResources.getDrawable(R.drawable.removedpng); } private LayoutInflater getLayoutInflater() { ContextWrapper c = new ContextWrapper(mContext) { private LayoutInflater mInflater; Loading tools/aapt2/Resource.h +6 −0 Original line number Diff line number Diff line Loading @@ -243,6 +243,12 @@ struct ResourceFile { // Exported symbols std::vector<SourcedResourceName> exported_symbols; // Flag status FlagStatus flag_status = FlagStatus::NoFlag; // Flag std::optional<FeatureFlagAttribute> flag; }; /** Loading tools/aapt2/ResourceParser.cpp +22 −28 Original line number Diff line number Diff line Loading @@ -546,8 +546,14 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, {"symbol", std::mem_fn(&ResourceParser::ParseSymbol)}, }); std::string resource_type = parser->element_name(); out_resource->flag = GetFlag(parser); std::string_view resource_type = parser->element_name(); if (auto flag = ParseFlag(xml::FindAttribute(parser, xml::kSchemaAndroid, "featureFlag"))) { if (options_.flag) { diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number())) << "Resource flag are not allowed both in the path and in the file"); return false; } out_resource->flag = std::move(flag); std::string error; auto flag_status = GetFlagStatus(out_resource->flag, options_.feature_flag_values, &error); if (flag_status) { Loading @@ -556,6 +562,10 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number())) << error); return false; } } else if (options_.flag) { out_resource->flag = options_.flag; out_resource->flag_status = options_.flag_status; } // The value format accepted for this resource. uint32_t resource_format = 0u; Loading @@ -571,7 +581,7 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, // Items have their type encoded in the type attribute. if (std::optional<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) { resource_type = std::string(maybe_type.value()); resource_type = maybe_type.value(); } else { diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number())) << "<item> must have a 'type' attribute"); Loading @@ -594,7 +604,7 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, // Bags have their type encoded in the type attribute. if (std::optional<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) { resource_type = std::string(maybe_type.value()); resource_type = maybe_type.value(); } else { diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number())) << "<bag> must have a 'type' attribute"); Loading Loading @@ -737,22 +747,6 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, return false; } std::optional<FeatureFlagAttribute> ResourceParser::GetFlag(xml::XmlPullParser* parser) { auto name = xml::FindAttribute(parser, xml::kSchemaAndroid, "featureFlag"); if (name) { FeatureFlagAttribute flag; if (name->starts_with('!')) { flag.negated = true; flag.name = name->substr(1); } else { flag.name = name.value(); } return flag; } else { return {}; } } bool ResourceParser::ParseItem(xml::XmlPullParser* parser, ParsedResource* out_resource, const uint32_t format) { Loading Loading @@ -1659,7 +1653,7 @@ bool ResourceParser::ParseArrayImpl(xml::XmlPullParser* parser, const std::string& element_namespace = parser->element_namespace(); const std::string& element_name = parser->element_name(); if (element_namespace.empty() && element_name == "item") { auto flag = GetFlag(parser); auto flag = ParseFlag(xml::FindAttribute(parser, xml::kSchemaAndroid, "featureFlag")); std::unique_ptr<Item> item = ParseXml(parser, typeMask, kNoRawString); if (!item) { diag_->Error(android::DiagMessage(item_source) << "could not parse array item"); Loading tools/aapt2/ResourceParser.h +5 −2 Original line number Diff line number Diff line Loading @@ -57,6 +57,11 @@ struct ResourceParserOptions { std::optional<Visibility::Level> visibility; FeatureFlagValues feature_flag_values; // The flag that should be applied to all resources parsed std::optional<FeatureFlagAttribute> flag; FlagStatus flag_status = FlagStatus::NoFlag; }; struct FlattenedXmlSubTree { Loading Loading @@ -85,8 +90,6 @@ class ResourceParser { private: DISALLOW_COPY_AND_ASSIGN(ResourceParser); std::optional<FeatureFlagAttribute> GetFlag(xml::XmlPullParser* parser); std::optional<FlattenedXmlSubTree> CreateFlattenSubTree(xml::XmlPullParser* parser); // Parses the XML subtree as a StyleString (flattened XML representation for strings with Loading tools/aapt2/ResourcesInternal.proto +5 −0 Original line number Diff line number Diff line Loading @@ -49,4 +49,9 @@ message CompiledFile { // Any symbols this file auto-generates/exports (eg. @+id/foo in an XML file). repeated Symbol exported_symbol = 5; // The status of the flag the file is behind if any uint32 flag_status = 6; bool flag_negated = 7; string flag_name = 8; } Loading
core/tests/resourceflaggingtests/src/com/android/resourceflaggingtests/ResourceFlaggingTest.java +39 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import android.util.DisplayMetrics; import android.view.LayoutInflater; import android.view.View; import android.widget.LinearLayout; import android.widget.TextView; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; Loading Loading @@ -103,6 +104,26 @@ public class ResourceFlaggingTest { assertThat(mResources.getIntArray(R.array.intarr1)).isEqualTo(new int[]{1, 2, 3}); } @Test public void testDirectoryEnabledFlag() { assertThat(mResources.getBoolean(R.bool.bool8)).isTrue(); } @Test public void testDirectoryDisabledFlag() { assertThat(mResources.getBoolean(R.bool.bool7)).isTrue(); } @Test public void testDirectoryNegatedEnabledFlag() { assertThat(mResources.getBoolean(R.bool.bool9)).isTrue(); } @Test public void testDirectoryNegatedDisabledFlag() { assertThat(mResources.getBoolean(R.bool.bool10)).isTrue(); } @Test public void testLayoutWithDisabledElements() { LinearLayout ll = (LinearLayout) getLayoutInflater().inflate(R.layout.layout1, null); Loading @@ -112,6 +133,24 @@ public class ResourceFlaggingTest { assertThat((View) ll.findViewById(R.id.text2)).isNotNull(); } @Test public void testEnabledFlagLayoutOverrides() { LinearLayout ll = (LinearLayout) getLayoutInflater().inflate(R.layout.layout3, null); assertThat(ll).isNotNull(); assertThat((View) ll.findViewById(R.id.text1)).isNotNull(); assertThat(((TextView) ll.findViewById(R.id.text1)).getText()).isEqualTo("foobar"); } @Test(expected = Resources.NotFoundException.class) public void testDisabledLayout() { getLayoutInflater().inflate(R.layout.layout2, null); } @Test(expected = Resources.NotFoundException.class) public void testDisabledDrawable() { mResources.getDrawable(R.drawable.removedpng); } private LayoutInflater getLayoutInflater() { ContextWrapper c = new ContextWrapper(mContext) { private LayoutInflater mInflater; Loading
tools/aapt2/Resource.h +6 −0 Original line number Diff line number Diff line Loading @@ -243,6 +243,12 @@ struct ResourceFile { // Exported symbols std::vector<SourcedResourceName> exported_symbols; // Flag status FlagStatus flag_status = FlagStatus::NoFlag; // Flag std::optional<FeatureFlagAttribute> flag; }; /** Loading
tools/aapt2/ResourceParser.cpp +22 −28 Original line number Diff line number Diff line Loading @@ -546,8 +546,14 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, {"symbol", std::mem_fn(&ResourceParser::ParseSymbol)}, }); std::string resource_type = parser->element_name(); out_resource->flag = GetFlag(parser); std::string_view resource_type = parser->element_name(); if (auto flag = ParseFlag(xml::FindAttribute(parser, xml::kSchemaAndroid, "featureFlag"))) { if (options_.flag) { diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number())) << "Resource flag are not allowed both in the path and in the file"); return false; } out_resource->flag = std::move(flag); std::string error; auto flag_status = GetFlagStatus(out_resource->flag, options_.feature_flag_values, &error); if (flag_status) { Loading @@ -556,6 +562,10 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number())) << error); return false; } } else if (options_.flag) { out_resource->flag = options_.flag; out_resource->flag_status = options_.flag_status; } // The value format accepted for this resource. uint32_t resource_format = 0u; Loading @@ -571,7 +581,7 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, // Items have their type encoded in the type attribute. if (std::optional<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) { resource_type = std::string(maybe_type.value()); resource_type = maybe_type.value(); } else { diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number())) << "<item> must have a 'type' attribute"); Loading @@ -594,7 +604,7 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, // Bags have their type encoded in the type attribute. if (std::optional<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) { resource_type = std::string(maybe_type.value()); resource_type = maybe_type.value(); } else { diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number())) << "<bag> must have a 'type' attribute"); Loading Loading @@ -737,22 +747,6 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, return false; } std::optional<FeatureFlagAttribute> ResourceParser::GetFlag(xml::XmlPullParser* parser) { auto name = xml::FindAttribute(parser, xml::kSchemaAndroid, "featureFlag"); if (name) { FeatureFlagAttribute flag; if (name->starts_with('!')) { flag.negated = true; flag.name = name->substr(1); } else { flag.name = name.value(); } return flag; } else { return {}; } } bool ResourceParser::ParseItem(xml::XmlPullParser* parser, ParsedResource* out_resource, const uint32_t format) { Loading Loading @@ -1659,7 +1653,7 @@ bool ResourceParser::ParseArrayImpl(xml::XmlPullParser* parser, const std::string& element_namespace = parser->element_namespace(); const std::string& element_name = parser->element_name(); if (element_namespace.empty() && element_name == "item") { auto flag = GetFlag(parser); auto flag = ParseFlag(xml::FindAttribute(parser, xml::kSchemaAndroid, "featureFlag")); std::unique_ptr<Item> item = ParseXml(parser, typeMask, kNoRawString); if (!item) { diag_->Error(android::DiagMessage(item_source) << "could not parse array item"); Loading
tools/aapt2/ResourceParser.h +5 −2 Original line number Diff line number Diff line Loading @@ -57,6 +57,11 @@ struct ResourceParserOptions { std::optional<Visibility::Level> visibility; FeatureFlagValues feature_flag_values; // The flag that should be applied to all resources parsed std::optional<FeatureFlagAttribute> flag; FlagStatus flag_status = FlagStatus::NoFlag; }; struct FlattenedXmlSubTree { Loading Loading @@ -85,8 +90,6 @@ class ResourceParser { private: DISALLOW_COPY_AND_ASSIGN(ResourceParser); std::optional<FeatureFlagAttribute> GetFlag(xml::XmlPullParser* parser); std::optional<FlattenedXmlSubTree> CreateFlattenSubTree(xml::XmlPullParser* parser); // Parses the XML subtree as a StyleString (flattened XML representation for strings with Loading
tools/aapt2/ResourcesInternal.proto +5 −0 Original line number Diff line number Diff line Loading @@ -49,4 +49,9 @@ message CompiledFile { // Any symbols this file auto-generates/exports (eg. @+id/foo in an XML file). repeated Symbol exported_symbol = 5; // The status of the flag the file is behind if any uint32 flag_status = 6; bool flag_negated = 7; string flag_name = 8; }