diff options
author | 2022-11-21 14:56:38 +0000 | |
---|---|---|
committer | 2022-11-23 00:10:11 +0000 | |
commit | 7a11a7b0cc53f930963df001d73103bde18b7ba4 (patch) | |
tree | 187d9143be8eaab5ece40df09d068563caa50797 | |
parent | e0771d4a9d4d7035570473aedf986dbd81ba8da2 (diff) |
Make the entry group type explicit
Bug: 254631418
Test: atest --no-bazel-mode CtsSafetyCenterTestCases
Change-Id: I3980e80dcb5adb5ee945d67a05025a929d546d5a
16 files changed, 775 insertions, 162 deletions
diff --git a/SafetyCenter/Config/java/com/android/safetycenter/config/SafetyCenterConfigParser.java b/SafetyCenter/Config/java/com/android/safetycenter/config/SafetyCenterConfigParser.java index adb7260ba..504fcfc3c 100644 --- a/SafetyCenter/Config/java/com/android/safetycenter/config/SafetyCenterConfigParser.java +++ b/SafetyCenter/Config/java/com/android/safetycenter/config/SafetyCenterConfigParser.java @@ -60,6 +60,7 @@ public final class SafetyCenterConfigParser { private static final String ATTR_SAFETY_SOURCES_GROUP_TITLE = "title"; private static final String ATTR_SAFETY_SOURCES_GROUP_SUMMARY = "summary"; private static final String ATTR_SAFETY_SOURCES_GROUP_STATELESS_ICON_TYPE = "statelessIconType"; + private static final String ATTR_SAFETY_SOURCES_GROUP_TYPE = "type"; private static final String ATTR_SAFETY_SOURCE_ID = "id"; private static final String ATTR_SAFETY_SOURCE_PACKAGE_NAME = "packageName"; private static final String ATTR_SAFETY_SOURCE_TITLE = "title"; @@ -77,6 +78,9 @@ public final class SafetyCenterConfigParser { private static final String ATTR_SAFETY_SOURCE_DEDUPLICATION_GROUP = "deduplicationGroup"; private static final String ENUM_STATELESS_ICON_TYPE_NONE = "none"; private static final String ENUM_STATELESS_ICON_TYPE_PRIVACY = "privacy"; + private static final String ENUM_GROUP_TYPE_COLLAPSIBLE = "collapsible"; + private static final String ENUM_GROUP_TYPE_HIDDEN = "hidden"; + private static final String ENUM_GROUP_TYPE_RIGID = "rigid"; private static final String ENUM_PROFILE_PRIMARY = "primary_profile_only"; private static final String ENUM_PROFILE_ALL = "all_profiles"; private static final String ENUM_INITIAL_DISPLAY_STATE_ENABLED = "enabled"; @@ -187,6 +191,18 @@ public final class SafetyCenterConfigParser { parser.getAttributeName(i), resources)); break; + case ATTR_SAFETY_SOURCES_GROUP_TYPE: + if (SdkLevel.isAtLeastU()) { + builder.setType( + parseGroupType( + parser.getAttributeValue(i), + name, + parser.getAttributeName(i), + resources)); + } else { + throw attributeUnexpected(name, parser.getAttributeName(i)); + } + break; default: throw attributeUnexpected(name, parser.getAttributeName(i)); } @@ -529,6 +545,25 @@ public final class SafetyCenterConfigParser { } } + private static int parseGroupType( + @NonNull String valueString, + @NonNull String parent, + @NonNull String name, + @NonNull Resources resources) + throws ParseException { + String valueToParse = getValueToParse(valueString, parent, name, resources); + switch (valueToParse) { + case ENUM_GROUP_TYPE_COLLAPSIBLE: + return SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE; + case ENUM_GROUP_TYPE_RIGID: + return SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_RIGID; + case ENUM_GROUP_TYPE_HIDDEN: + return SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN; + default: + throw attributeInvalid(valueToParse, parent, name); + } + } + private static int parseProfile( @NonNull String valueString, @NonNull String parent, diff --git a/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigInvalidTest.kt b/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigInvalidTest.kt index 107d6e35a..3d4ce37d9 100644 --- a/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigInvalidTest.kt +++ b/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigInvalidTest.kt @@ -17,8 +17,6 @@ package com.android.safetycenter.config import android.content.Context -import android.os.Build -import androidx.annotation.RequiresApi import androidx.test.core.app.ApplicationProvider.getApplicationContext import com.android.modules.utils.build.SdkLevel import com.android.safetycenter.config.tests.R @@ -36,7 +34,8 @@ class ParserConfigInvalidTest { private val testName: String, val configResourceId: Int, val errorMessage: String, - val causeErrorMessage: String? + val causeErrorMessage: String?, + val includeCondition: Boolean = true ) { override fun toString() = testName } @@ -62,28 +61,6 @@ class ParserConfigInvalidTest { @JvmStatic @Parameterized.Parameters(name = "{0}") fun parameters() = - if (SdkLevel.isAtLeastU()) { - parametersUpsideDownCake() - } else { - parametersTiramisu() - } - - @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) - fun parametersUpsideDownCake(): Array<Params> = - parametersTiramisu() + - arrayOf( - Params( - "ConfigStaticSafetySourceWithNotifications", - R.raw.config_static_safety_source_with_notifications, - "Element static-safety-source invalid", - "Prohibited attribute notificationsAllowed present"), - Params( - "ConfigStaticSafetySourceWithDeduplicationGroups", - R.raw.config_static_safety_source_with_deduplication_groups, - "Element static-safety-source invalid", - "Prohibited attribute deduplicationGroup present")) - - fun parametersTiramisu(): Array<Params> = arrayOf( Params( "ConfigDynamicSafetySourceAllDisabledNoWork", @@ -252,6 +229,13 @@ class ParserConfigInvalidTest { "Element safety-sources-config missing", null), Params( + "ConfigSafetySourcesGroupCollapsibleWithOnlyIssueOnly", + R.raw.config_safety_sources_group_collapsible_with_only_issue_only, + "Element safety-sources-group invalid", + "Safety sources groups containing only sources of type issue-only must be of " + + "type hidden", + SdkLevel.isAtLeastU()), + Params( "ConfigSafetySourcesGroupDuplicateId", R.raw.config_safety_sources_group_duplicate_id, "Element safety-sources-config invalid", @@ -262,6 +246,20 @@ class ParserConfigInvalidTest { "Element safety-sources-group invalid", "Safety sources group empty"), Params( + "ConfigSafetySourcesGroupHiddenWithOnlyDynamic", + R.raw.config_safety_sources_group_hidden_with_only_dynamic, + "Element safety-sources-group invalid", + "Safety sources groups of type hidden can only contain sources of type " + + "issue-only", + SdkLevel.isAtLeastU()), + Params( + "ConfigSafetySourcesGroupHiddenWithOnlyStatic", + R.raw.config_safety_sources_group_hidden_with_only_static, + "Element safety-sources-group invalid", + "Safety sources groups of type hidden can only contain sources of type " + + "issue-only", + SdkLevel.isAtLeastU()), + Params( "ConfigSafetySourcesGroupInvalidIcon", R.raw.config_safety_sources_group_invalid_icon, "Attribute value \"invalid\" in safety-sources-group.statelessIconType invalid", @@ -277,6 +275,13 @@ class ParserConfigInvalidTest { "Element safety-sources-group invalid", "Required attribute title missing"), Params( + "ConfigSafetySourcesGroupRigidWithOnlyIssueOnly", + R.raw.config_safety_sources_group_rigid_with_only_issue_only, + "Element safety-sources-group invalid", + "Safety sources groups containing only sources of type issue-only must be of " + + "type hidden", + SdkLevel.isAtLeastU()), + Params( "ConfigStaticSafetySourceDuplicateKey", R.raw.config_static_safety_source_duplicate_key, "Element safety-sources-config invalid", @@ -307,6 +312,12 @@ class ParserConfigInvalidTest { "Element static-safety-source invalid", "Required attribute title missing"), Params( + "ConfigStaticSafetySourceWithDeduplicationGroups", + R.raw.config_static_safety_source_with_deduplication_groups, + "Element static-safety-source invalid", + "Prohibited attribute deduplicationGroup present", + SdkLevel.isAtLeastU()), + Params( "ConfigStaticSafetySourceWithDisplay", R.raw.config_static_safety_source_with_display, "Element static-safety-source invalid", @@ -317,6 +328,12 @@ class ParserConfigInvalidTest { "Element static-safety-source invalid", "Prohibited attribute loggingAllowed present"), Params( + "ConfigStaticSafetySourceWithNotifications", + R.raw.config_static_safety_source_with_notifications, + "Element static-safety-source invalid", + "Prohibited attribute notificationsAllowed present", + SdkLevel.isAtLeastU()), + Params( "ConfigStaticSafetySourceWithPackage", R.raw.config_static_safety_source_with_package, "Element static-safety-source invalid", @@ -372,5 +389,6 @@ class ParserConfigInvalidTest { "Resource name \"@com.android.safetycenter.config.tests:string/missing\" in " + "safety-sources-group.title missing or invalid", null)) + .filter { it.includeCondition } } } diff --git a/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigValidTest.kt b/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigValidTest.kt index 34d91e6bf..bd5137cf4 100644 --- a/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigValidTest.kt +++ b/SafetyCenter/Config/tests/java/com/android/safetycenter/config/ParserConfigValidTest.kt @@ -200,6 +200,94 @@ class ParserConfigValidTest { .setProfile(SafetySource.PROFILE_PRIMARY) .build()) .build()) + .apply { + if (SdkLevel.isAtLeastU()) { + addSafetySourcesGroup( + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE) + .setId("collapsible_barebone") + .setTitleResId(R.string.reference) + .addSafetySource( + SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_STATIC) + .setId("collapsible_barebone_source") + .setTitleResId(R.string.reference) + .setIntentAction("intent") + .setProfile(SafetySource.PROFILE_PRIMARY) + .build()) + .build()) + addSafetySourcesGroup( + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE) + .setId("collapsible_all_optional") + .setTitleResId(R.string.reference) + .setSummaryResId(R.string.reference) + .setStatelessIconType( + SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) + .addSafetySource( + SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_STATIC) + .setId("collapsible_all_optional_source") + .setTitleResId(R.string.reference) + .setIntentAction("intent") + .setProfile(SafetySource.PROFILE_PRIMARY) + .build()) + .build()) + addSafetySourcesGroup( + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_RIGID) + .setId("rigid_barebone") + .setTitleResId(R.string.reference) + .addSafetySource( + SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_STATIC) + .setId("rigid_barebone_source") + .setTitleResId(R.string.reference) + .setIntentAction("intent") + .setProfile(SafetySource.PROFILE_PRIMARY) + .build()) + .build()) + addSafetySourcesGroup( + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_RIGID) + .setId("rigid_all_optional") + .setTitleResId(R.string.reference) + .setSummaryResId(R.string.reference) + .setStatelessIconType( + SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) + .addSafetySource( + SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_STATIC) + .setId("rigid_all_optional_source") + .setTitleResId(R.string.reference) + .setIntentAction("intent") + .setProfile(SafetySource.PROFILE_PRIMARY) + .build()) + .build()) + addSafetySourcesGroup( + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN) + .setId("hidden_barebone") + .addSafetySource( + SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY) + .setId("hidden_barebone_source") + .setPackageName("package") + .setProfile(SafetySource.PROFILE_PRIMARY) + .build()) + .build()) + addSafetySourcesGroup( + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN) + .setId("hidden_all_optional") + .setTitleResId(R.string.reference) + .setSummaryResId(R.string.reference) + .setStatelessIconType( + SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) + .addSafetySource( + SafetySource.Builder(SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY) + .setId("hidden_all_optional_source") + .setPackageName("package") + .setProfile(SafetySource.PROFILE_PRIMARY) + .build()) + .build()) + } + } .build() assertThat(actual).isEqualTo(expected) } diff --git a/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_collapsible_with_only_issue_only.xml b/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_collapsible_with_only_issue_only.xml new file mode 100644 index 000000000..effe379f4 --- /dev/null +++ b/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_collapsible_with_only_issue_only.xml @@ -0,0 +1,13 @@ +<safety-center-config> + <safety-sources-config> + <safety-sources-group + type="collapsible" + id="id" + title="@com.android.safetycenter.config.tests:string/reference"> + <issue-only-safety-source + id="id" + packageName="package" + profile="primary_profile_only"/> + </safety-sources-group> + </safety-sources-config> +</safety-center-config> diff --git a/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_hidden_with_only_dynamic.xml b/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_hidden_with_only_dynamic.xml new file mode 100644 index 000000000..4be2c75ff --- /dev/null +++ b/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_hidden_with_only_dynamic.xml @@ -0,0 +1,15 @@ +<safety-center-config> + <safety-sources-config> + <safety-sources-group + type="hidden" + id="id"> + <dynamic-safety-source + id="id" + packageName="package" + title="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="primary_profile_only"/> + </safety-sources-group> + </safety-sources-config> +</safety-center-config> diff --git a/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_hidden_with_only_static.xml b/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_hidden_with_only_static.xml new file mode 100644 index 000000000..65f9f0ac9 --- /dev/null +++ b/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_hidden_with_only_static.xml @@ -0,0 +1,14 @@ +<safety-center-config> + <safety-sources-config> + <safety-sources-group + type="hidden" + id="id"> + <static-safety-source + id="id" + title="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="primary_profile_only"/> + </safety-sources-group> + </safety-sources-config> +</safety-center-config> diff --git a/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_rigid_with_only_issue_only.xml b/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_rigid_with_only_issue_only.xml new file mode 100644 index 000000000..1d95a55ec --- /dev/null +++ b/SafetyCenter/Config/tests/res/raw-v34/config_safety_sources_group_rigid_with_only_issue_only.xml @@ -0,0 +1,13 @@ +<safety-center-config> + <safety-sources-config> + <safety-sources-group + type="rigid" + id="id" + title="@com.android.safetycenter.config.tests:string/reference"> + <issue-only-safety-source + id="id" + packageName="package" + profile="primary_profile_only"/> + </safety-sources-group> + </safety-sources-config> +</safety-center-config> diff --git a/SafetyCenter/Config/tests/res/raw-v34/config_valid.xml b/SafetyCenter/Config/tests/res/raw-v34/config_valid.xml index eec65ec7e..5b88dd5e3 100644 --- a/SafetyCenter/Config/tests/res/raw-v34/config_valid.xml +++ b/SafetyCenter/Config/tests/res/raw-v34/config_valid.xml @@ -118,5 +118,68 @@ intentAction="intent" profile="primary_profile_only"/> </safety-sources-group> + <safety-sources-group + type="collapsible" + id="collapsible_barebone" + title="@com.android.safetycenter.config.tests:string/reference"> + <static-safety-source + id="collapsible_barebone_source" + title="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="primary_profile_only"/> + </safety-sources-group> + <safety-sources-group + type="collapsible" + id="collapsible_all_optional" + title="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + statelessIconType="privacy"> + <static-safety-source + id="collapsible_all_optional_source" + title="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="primary_profile_only"/> + </safety-sources-group> + <safety-sources-group + type="rigid" + id="rigid_barebone" + title="@com.android.safetycenter.config.tests:string/reference"> + <static-safety-source + id="rigid_barebone_source" + title="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="primary_profile_only"/> + </safety-sources-group> + <safety-sources-group + type="rigid" + id="rigid_all_optional" + title="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + statelessIconType="privacy"> + <static-safety-source + id="rigid_all_optional_source" + title="@com.android.safetycenter.config.tests:string/reference" + intentAction="intent" + profile="primary_profile_only"/> + </safety-sources-group> + <safety-sources-group + type="hidden" + id="hidden_barebone"> + <issue-only-safety-source + id="hidden_barebone_source" + packageName="package" + profile="primary_profile_only"/> + </safety-sources-group> + <safety-sources-group + type="hidden" + id="hidden_all_optional" + title="@com.android.safetycenter.config.tests:string/reference" + summary="@com.android.safetycenter.config.tests:string/reference" + statelessIconType="privacy"> + <issue-only-safety-source + id="hidden_all_optional_source" + packageName="package" + profile="primary_profile_only"/> + </safety-sources-group> </safety-sources-config> </safety-center-config> diff --git a/framework-s/api/system-current.txt b/framework-s/api/system-current.txt index 006374367..9fda15d51 100644 --- a/framework-s/api/system-current.txt +++ b/framework-s/api/system-current.txt @@ -552,6 +552,7 @@ package android.safetycenter.config { method @NonNull public android.safetycenter.config.SafetySourcesGroup.Builder setStatelessIconType(int); method @NonNull public android.safetycenter.config.SafetySourcesGroup.Builder setSummaryResId(@StringRes int); method @NonNull public android.safetycenter.config.SafetySourcesGroup.Builder setTitleResId(@StringRes int); + method @NonNull public android.safetycenter.config.SafetySourcesGroup.Builder setType(int); } } diff --git a/framework-s/java/android/safetycenter/config/SafetySource.java b/framework-s/java/android/safetycenter/config/SafetySource.java index d3d9cb9f7..0066b62f8 100644 --- a/framework-s/java/android/safetycenter/config/SafetySource.java +++ b/framework-s/java/android/safetycenter/config/SafetySource.java @@ -817,19 +817,22 @@ public final class SafetySource implements Parcelable { */ @NonNull public SafetySource build() { - if (mType != SAFETY_SOURCE_TYPE_STATIC - && mType != SAFETY_SOURCE_TYPE_DYNAMIC - && mType != SAFETY_SOURCE_TYPE_ISSUE_ONLY) { + int type = mType; + if (type != SAFETY_SOURCE_TYPE_STATIC + && type != SAFETY_SOURCE_TYPE_DYNAMIC + && type != SAFETY_SOURCE_TYPE_ISSUE_ONLY) { throw new IllegalStateException("Unexpected type"); } - boolean isStatic = mType == SAFETY_SOURCE_TYPE_STATIC; - boolean isDynamic = mType == SAFETY_SOURCE_TYPE_DYNAMIC; - boolean isIssueOnly = mType == SAFETY_SOURCE_TYPE_ISSUE_ONLY; + boolean isStatic = type == SAFETY_SOURCE_TYPE_STATIC; + boolean isDynamic = type == SAFETY_SOURCE_TYPE_DYNAMIC; + boolean isIssueOnly = type == SAFETY_SOURCE_TYPE_ISSUE_ONLY; - BuilderUtils.validateAttribute(mId, "id", true, false); + String id = mId; + BuilderUtils.validateAttribute(id, "id", true, false); + String packageName = mPackageName; BuilderUtils.validateAttribute( - mPackageName, "packageName", isDynamic || isIssueOnly, isStatic); + packageName, "packageName", isDynamic || isIssueOnly, isStatic); int initialDisplayState = BuilderUtils.validateIntDef( @@ -877,8 +880,9 @@ public final class SafetySource implements Parcelable { BuilderUtils.validateResId( mSummaryResId, "summary", isDynamicNotHidden, isIssueOnly); + String intentAction = mIntentAction; BuilderUtils.validateAttribute( - mIntentAction, + intentAction, "intentAction", (isDynamic && isEnabled) || isStatic, isIssueOnly); @@ -903,6 +907,7 @@ public final class SafetySource implements Parcelable { isStatic, false); + String deduplicationGroup = mDeduplicationGroup; boolean notificationsAllowed = false; if (SdkLevel.isAtLeastU()) { notificationsAllowed = @@ -914,17 +919,17 @@ public final class SafetySource implements Parcelable { false); BuilderUtils.validateAttribute( - mDeduplicationGroup, "deduplicationGroup", false, isStatic); + deduplicationGroup, "deduplicationGroup", false, isStatic); } return new SafetySource( - mType, - mId, - mPackageName, + type, + id, + packageName, titleResId, titleForWorkResId, summaryResId, - mIntentAction, + intentAction, profile, initialDisplayState, maxSeverityLevel, @@ -932,7 +937,7 @@ public final class SafetySource implements Parcelable { loggingAllowed, refreshOnPageOpenAllowed, notificationsAllowed, - mDeduplicationGroup); + deduplicationGroup); } } } diff --git a/framework-s/java/android/safetycenter/config/SafetySourcesGroup.java b/framework-s/java/android/safetycenter/config/SafetySourcesGroup.java index 2868ee7ec..9e53feac2 100644 --- a/framework-s/java/android/safetycenter/config/SafetySourcesGroup.java +++ b/framework-s/java/android/safetycenter/config/SafetySourcesGroup.java @@ -17,6 +17,7 @@ package android.safetycenter.config; import static android.os.Build.VERSION_CODES.TIRAMISU; +import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; import static java.util.Collections.unmodifiableList; import static java.util.Objects.requireNonNull; @@ -32,6 +33,8 @@ import android.os.Parcelable; import androidx.annotation.RequiresApi; +import com.android.modules.utils.build.SdkLevel; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -116,6 +119,9 @@ public final class SafetySourcesGroup implements Parcelable { for (int i = 0; i < safetySources.size(); i++) { builder.addSafetySource(safetySources.get(i)); } + if (SdkLevel.isAtLeastU()) { + builder.setType(in.readInt()); + } return builder.build(); } @@ -125,6 +131,7 @@ public final class SafetySourcesGroup implements Parcelable { } }; + @SafetySourceGroupType private final int mType; @NonNull private final String mId; @StringRes private final int mTitleResId; @StringRes private final int mSummaryResId; @@ -132,11 +139,13 @@ public final class SafetySourcesGroup implements Parcelable { @NonNull private final List<SafetySource> mSafetySources; private SafetySourcesGroup( + @SafetySourceGroupType int type, @NonNull String id, @StringRes int titleResId, @StringRes int summaryResId, @StatelessIconType int statelessIconType, @NonNull List<SafetySource> safetySources) { + mType = type; mId = id; mTitleResId = titleResId; mSummaryResId = summaryResId; @@ -144,23 +153,10 @@ public final class SafetySourcesGroup implements Parcelable { mSafetySources = safetySources; } - /** - * Returns the type of this safety sources group. - * - * <p>The type is inferred according to the state of certain fields. If no title is provided - * when building the group, the group is of type hidden. If a title is provided but no summary - * or stateless icon are provided when building the group, the group is of type rigid. - * Otherwise, the group is of type collapsible. - */ + /** Returns the type of this safety sources group. */ @SafetySourceGroupType public int getType() { - if (mTitleResId == Resources.ID_NULL) { - return SAFETY_SOURCES_GROUP_TYPE_HIDDEN; - } - if (mSummaryResId != Resources.ID_NULL || mStatelessIconType != STATELESS_ICON_TYPE_NONE) { - return SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE; - } - return SAFETY_SOURCES_GROUP_TYPE_RIGID; + return mType; } /** @@ -224,7 +220,8 @@ public final class SafetySourcesGroup implements Parcelable { if (this == o) return true; if (!(o instanceof SafetySourcesGroup)) return false; SafetySourcesGroup that = (SafetySourcesGroup) o; - return Objects.equals(mId, that.mId) + return mType == that.mType + && Objects.equals(mId, that.mId) && mTitleResId == that.mTitleResId && mSummaryResId == that.mSummaryResId && mStatelessIconType == that.mStatelessIconType @@ -233,13 +230,16 @@ public final class SafetySourcesGroup implements Parcelable { @Override public int hashCode() { - return Objects.hash(mId, mTitleResId, mSummaryResId, mStatelessIconType, mSafetySources); + return Objects.hash( + mType, mId, mTitleResId, mSummaryResId, mStatelessIconType, mSafetySources); } @Override public String toString() { return "SafetySourcesGroup{" - + "mId=" + + "mType=" + + mType + + ", mId=" + mId + ", mTitleResId=" + mTitleResId @@ -264,6 +264,9 @@ public final class SafetySourcesGroup implements Parcelable { dest.writeInt(mSummaryResId); dest.writeInt(mStatelessIconType); dest.writeTypedList(mSafetySources); + if (SdkLevel.isAtLeastU()) { + dest.writeInt(mType); + } } /** Builder class for {@link SafetySourcesGroup}. */ @@ -271,6 +274,7 @@ public final class SafetySourcesGroup implements Parcelable { private final List<SafetySource> mSafetySources = new ArrayList<>(); + @Nullable @SafetySourceGroupType private Integer mType; @Nullable private String mId; @Nullable @StringRes private Integer mTitleResId; @Nullable @StringRes private Integer mSummaryResId; @@ -280,6 +284,22 @@ public final class SafetySourcesGroup implements Parcelable { public Builder() {} /** + * Sets the type of this safety sources group. + * + * <p>If the type is not explicitly set, the type is inferred according to the state of + * certain fields. If no title is provided when building the group, the group is of type + * hidden. If a title is provided but no summary or stateless icon are provided when + * building the group, the group is of type stateless. Otherwise, the group is of type + * stateful. + */ + @NonNull + @RequiresApi(UPSIDE_DOWN_CAKE) + public Builder setType(@StatelessIconType int type) { + mType = type; + return this; + } + + /** * Sets the id of this safety sources group. * * <p>The id must be unique among safety sources groups in a Safety Center configuration. @@ -351,22 +371,16 @@ public final class SafetySourcesGroup implements Parcelable { */ @NonNull public SafetySourcesGroup build() { - BuilderUtils.validateAttribute(mId, "id", true, false); + String id = mId; + BuilderUtils.validateAttribute(id, "id", true, false); + List<SafetySource> safetySources = unmodifiableList(new ArrayList<>(mSafetySources)); if (safetySources.isEmpty()) { throw new IllegalStateException("Safety sources group empty"); } - boolean titleRequired = false; - int safetySourcesSize = safetySources.size(); - for (int i = 0; i < safetySourcesSize; i++) { - int type = safetySources.get(i).getType(); - if (type != SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY) { - titleRequired = true; - break; - } - } - int titleResId = BuilderUtils.validateResId(mTitleResId, "title", titleRequired, false); + int summaryResId = BuilderUtils.validateResId(mSummaryResId, "summary", false, false); + int statelessIconType = BuilderUtils.validateIntDef( mStatelessIconType, @@ -376,8 +390,53 @@ public final class SafetySourcesGroup implements Parcelable { STATELESS_ICON_TYPE_NONE, STATELESS_ICON_TYPE_NONE, STATELESS_ICON_TYPE_PRIVACY); + + boolean hasOnlyIssueOnlySources = true; + int safetySourcesSize = safetySources.size(); + for (int i = 0; i < safetySourcesSize; i++) { + int type = safetySources.get(i).getType(); + if (type != SafetySource.SAFETY_SOURCE_TYPE_ISSUE_ONLY) { + hasOnlyIssueOnlySources = false; + break; + } + } + + int inferredGroupType = SAFETY_SOURCES_GROUP_TYPE_RIGID; + if (hasOnlyIssueOnlySources) { + inferredGroupType = SAFETY_SOURCES_GROUP_TYPE_HIDDEN; + } else if (summaryResId != Resources.ID_NULL + || statelessIconType != Resources.ID_NULL) { + inferredGroupType = SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE; + } + int type = + BuilderUtils.validateIntDef( + mType, + "type", + false, + false, + inferredGroupType, + SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE, + SAFETY_SOURCES_GROUP_TYPE_RIGID, + SAFETY_SOURCES_GROUP_TYPE_HIDDEN); + if (type == SAFETY_SOURCES_GROUP_TYPE_HIDDEN && !hasOnlyIssueOnlySources) { + throw new IllegalStateException( + "Safety sources groups of type hidden can only contain sources of type " + + "issue-only"); + } + if (type != SAFETY_SOURCES_GROUP_TYPE_HIDDEN && hasOnlyIssueOnlySources) { + throw new IllegalStateException( + "Safety sources groups containing only sources of type issue-only must be " + + "of type hidden"); + } + + boolean isCollapsible = type == SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE; + boolean isRigid = type == SAFETY_SOURCES_GROUP_TYPE_RIGID; + int titleResId = + BuilderUtils.validateResId( + mTitleResId, "title", isCollapsible || isCollapsible, false); + return new SafetySourcesGroup( - mId, titleResId, summaryResId, statelessIconType, safetySources); + type, id, titleResId, summaryResId, statelessIconType, safetySources); } } } diff --git a/framework-s/java/android/safetycenter/config/safety_center_config-v34.xsd b/framework-s/java/android/safetycenter/config/safety_center_config-v34.xsd index 75b31c166..492cff9ef 100644 --- a/framework-s/java/android/safetycenter/config/safety_center_config-v34.xsd +++ b/framework-s/java/android/safetycenter/config/safety_center_config-v34.xsd @@ -47,6 +47,8 @@ <xsd:attribute name="summary" type="runtimeStringResourceName"/> <xsd:attribute name="statelessIconType" type="statelessIconTypeOrStringResourceName" default="none"/> + <!-- type is inferred from other attributes and the group content if omitted --> + <xsd:attribute name="type" type="groupTypeOrStringResourceName"/> </xsd:complexType> <xsd:complexType name="dynamic-safety-source"> @@ -166,6 +168,21 @@ </xsd:restriction> </xsd:simpleType> + <xsd:simpleType name="groupTypeOrStringResourceName"> + <!-- String resource names will be resolved only once at parse time. --> + <!-- Locale changes and device config changes will be ignored. --> + <!-- The value of the string resource must be of type groupType. --> + <xsd:union memberTypes="stringResourceName groupType"/> + </xsd:simpleType> + + <xsd:simpleType name="groupType"> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="collapsible"/> + <xsd:enumeration value="rigid"/> + <xsd:enumeration value="hidden"/> + </xsd:restriction> + </xsd:simpleType> + <xsd:simpleType name="runtimeStringResourceName"> <!-- String resource names will be resolved at runtime whenever the string value is used. --> <xsd:union memberTypes="stringResourceName"/> diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt index f833ffc98..bdbde0f9d 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/SafetyCenterManagerTest.kt @@ -546,11 +546,9 @@ class SafetyCenterManagerTest { safetyCenterCtsData.safetyCenterStatusCritical(6), listOf( safetyCenterCtsData.safetyCenterIssueCritical(DYNAMIC_BAREBONE_ID), - safetyCenterCtsData.safetyCenterIssueCritical( - ISSUE_ONLY_BAREBONE_ID, attributionTitle = null), + safetyCenterCtsData.safetyCenterIssueCritical(ISSUE_ONLY_BAREBONE_ID), safetyCenterCtsData.safetyCenterIssueRecommendation(DYNAMIC_DISABLED_ID), - safetyCenterCtsData.safetyCenterIssueRecommendation( - ISSUE_ONLY_ALL_OPTIONAL_ID, attributionTitle = null), + safetyCenterCtsData.safetyCenterIssueRecommendation(ISSUE_ONLY_ALL_OPTIONAL_ID), safetyCenterCtsData.safetyCenterIssueInformation(DYNAMIC_IN_RIGID_ID), safetyCenterCtsData.safetyCenterIssueInformation(ISSUE_ONLY_IN_RIGID_ID)), listOf( diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetyCenterConfigTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetyCenterConfigTest.kt index 73aee4dff..7b0a35311 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetyCenterConfigTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetyCenterConfigTest.kt @@ -32,7 +32,8 @@ class SafetyCenterConfigTest { @Test fun getSafetySourcesGroups_returnsSafetySourcesGroups() { assertThat(BASE.safetySourcesGroups) - .containsExactly(SafetySourcesGroupTest.RIGID, SafetySourcesGroupTest.HIDDEN) + .containsExactly( + SafetySourcesGroupTest.RIGID_INFERRED, SafetySourcesGroupTest.HIDDEN_INFERRED) .inOrder() } @@ -41,7 +42,7 @@ class SafetyCenterConfigTest { val sourcesGroups = BASE.safetySourcesGroups assertFailsWith(UnsupportedOperationException::class) { - sourcesGroups.add(SafetySourcesGroupTest.COLLAPSIBLE_WITH_SUMMARY) + sourcesGroups.add(SafetySourcesGroupTest.COLLAPSIBLE_INFERRED_WITH_SUMMARY) } } @@ -49,15 +50,16 @@ class SafetyCenterConfigTest { fun builder_addSafetySourcesGroup_doesNotMutatePreviouslyBuiltInstance() { val safetyCenterConfigBuilder = SafetyCenterConfig.Builder() - .addSafetySourcesGroup(SafetySourcesGroupTest.RIGID) - .addSafetySourcesGroup(SafetySourcesGroupTest.HIDDEN) + .addSafetySourcesGroup(SafetySourcesGroupTest.RIGID_INFERRED) + .addSafetySourcesGroup(SafetySourcesGroupTest.HIDDEN_INFERRED) val sourceGroups = safetyCenterConfigBuilder.build().safetySourcesGroups safetyCenterConfigBuilder.addSafetySourcesGroup( - SafetySourcesGroupTest.COLLAPSIBLE_WITH_SUMMARY) + SafetySourcesGroupTest.COLLAPSIBLE_INFERRED_WITH_SUMMARY) assertThat(sourceGroups) - .containsExactly(SafetySourcesGroupTest.RIGID, SafetySourcesGroupTest.HIDDEN) + .containsExactly( + SafetySourcesGroupTest.RIGID_INFERRED, SafetySourcesGroupTest.HIDDEN_INFERRED) .inOrder() } @@ -77,13 +79,13 @@ class SafetyCenterConfigTest { .addEqualityGroup( BASE, SafetyCenterConfig.Builder() - .addSafetySourcesGroup(SafetySourcesGroupTest.RIGID) - .addSafetySourcesGroup(SafetySourcesGroupTest.HIDDEN) + .addSafetySourcesGroup(SafetySourcesGroupTest.RIGID_INFERRED) + .addSafetySourcesGroup(SafetySourcesGroupTest.HIDDEN_INFERRED) .build()) .addEqualityGroup( SafetyCenterConfig.Builder() - .addSafetySourcesGroup(SafetySourcesGroupTest.HIDDEN) - .addSafetySourcesGroup(SafetySourcesGroupTest.RIGID) + .addSafetySourcesGroup(SafetySourcesGroupTest.HIDDEN_INFERRED) + .addSafetySourcesGroup(SafetySourcesGroupTest.RIGID_INFERRED) .build()) .test() } @@ -91,8 +93,8 @@ class SafetyCenterConfigTest { companion object { private val BASE = SafetyCenterConfig.Builder() - .addSafetySourcesGroup(SafetySourcesGroupTest.RIGID) - .addSafetySourcesGroup(SafetySourcesGroupTest.HIDDEN) + .addSafetySourcesGroup(SafetySourcesGroupTest.RIGID_INFERRED) + .addSafetySourcesGroup(SafetySourcesGroupTest.HIDDEN_INFERRED) .build() } } diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourcesGroupTest.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourcesGroupTest.kt index f742b5d28..d241e9f4b 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourcesGroupTest.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/config/SafetySourcesGroupTest.kt @@ -17,9 +17,13 @@ package android.safetycenter.cts.config import android.content.res.Resources +import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE import android.safetycenter.config.SafetySourcesGroup +import androidx.annotation.RequiresApi import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.truth.os.ParcelableSubject.assertThat +import androidx.test.filters.SdkSuppress +import com.android.modules.utils.build.SdkLevel import com.android.permission.testing.EqualsHashCodeToStringTester import com.google.common.truth.Truth.assertThat import kotlin.test.assertFailsWith @@ -32,78 +36,156 @@ class SafetySourcesGroupTest { @Test fun getType_returnsType() { - assertThat(COLLAPSIBLE_WITH_SUMMARY.type) + assertThat(COLLAPSIBLE_INFERRED_WITH_SUMMARY.type) .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE) - assertThat(COLLAPSIBLE_WITH_ICON.type) + assertThat(COLLAPSIBLE_INFERRED_WITH_ICON.type) .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE) - assertThat(COLLAPSIBLE_WITH_BOTH.type) + assertThat(COLLAPSIBLE_INFERRED_WITH_BOTH.type) .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE) - assertThat(RIGID.type).isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_RIGID) - assertThat(HIDDEN.type).isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN) + assertThat(RIGID_INFERRED.type) + .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_RIGID) + assertThat(HIDDEN_INFERRED.type) + .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN) + if (SdkLevel.isAtLeastU()) { + assertThat(COLLAPSIBLE_BAREBONE.type) + .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE) + assertThat(COLLAPSIBLE_ALL_OPTIONAL.type) + .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE) + assertThat(RIGID_BAREBONE.type) + .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_RIGID) + assertThat(RIGID_ALL_OPTIONAL.type) + .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_RIGID) + assertThat(HIDDEN_BAREBONE.type) + .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN) + assertThat(HIDDEN_ALL_OPTIONAL.type) + .isEqualTo(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN) + } } @Test fun getId_returnsId() { - assertThat(COLLAPSIBLE_WITH_SUMMARY.id).isEqualTo(COLLAPSIBLE_WITH_SUMMARY_ID) - assertThat(COLLAPSIBLE_WITH_ICON.id).isEqualTo(COLLAPSIBLE_WITH_ICON_ID) - assertThat(COLLAPSIBLE_WITH_BOTH.id).isEqualTo(COLLAPSIBLE_WITH_BOTH_ID) - assertThat(RIGID.id).isEqualTo(RIGID_ID) - assertThat(HIDDEN.id).isEqualTo(HIDDEN_ID) + assertThat(COLLAPSIBLE_INFERRED_WITH_SUMMARY.id) + .isEqualTo(COLLAPSIBLE_INFERRED_WITH_SUMMARY_ID) + assertThat(COLLAPSIBLE_INFERRED_WITH_ICON.id).isEqualTo(COLLAPSIBLE_INFERRED_WITH_ICON_ID) + assertThat(COLLAPSIBLE_INFERRED_WITH_BOTH.id).isEqualTo(COLLAPSIBLE_INFERRED_WITH_BOTH_ID) + assertThat(RIGID_INFERRED.id).isEqualTo(RIGID_INFERRED_ID) + assertThat(HIDDEN_INFERRED.id).isEqualTo(HIDDEN_INFERRED_ID) + if (SdkLevel.isAtLeastU()) { + assertThat(COLLAPSIBLE_BAREBONE.id).isEqualTo(COLLAPSIBLE_BAREBONE_ID) + assertThat(COLLAPSIBLE_ALL_OPTIONAL.id).isEqualTo(COLLAPSIBLE_ALL_OPTIONAL_ID) + assertThat(RIGID_BAREBONE.id).isEqualTo(RIGID_BAREBONE_ID) + assertThat(RIGID_ALL_OPTIONAL.id).isEqualTo(RIGID_ALL_OPTIONAL_ID) + assertThat(HIDDEN_BAREBONE.id).isEqualTo(HIDDEN_BAREBONE_ID) + assertThat(HIDDEN_ALL_OPTIONAL.id).isEqualTo(HIDDEN_ALL_OPTIONAL_ID) + } } @Test fun getTitleResId_returnsTitleResId() { - assertThat(COLLAPSIBLE_WITH_SUMMARY.titleResId).isEqualTo(REFERENCE_RES_ID) - assertThat(COLLAPSIBLE_WITH_ICON.titleResId).isEqualTo(REFERENCE_RES_ID) - assertThat(COLLAPSIBLE_WITH_BOTH.titleResId).isEqualTo(REFERENCE_RES_ID) - assertThat(RIGID.titleResId).isEqualTo(REFERENCE_RES_ID) + assertThat(COLLAPSIBLE_INFERRED_WITH_SUMMARY.titleResId).isEqualTo(REFERENCE_RES_ID) + assertThat(COLLAPSIBLE_INFERRED_WITH_ICON.titleResId).isEqualTo(REFERENCE_RES_ID) + assertThat(COLLAPSIBLE_INFERRED_WITH_BOTH.titleResId).isEqualTo(REFERENCE_RES_ID) + assertThat(RIGID_INFERRED.titleResId).isEqualTo(REFERENCE_RES_ID) // This is not an enforced invariant, titleResId should just be ignored for hidden groups - assertThat(HIDDEN.titleResId).isEqualTo(Resources.ID_NULL) + assertThat(HIDDEN_INFERRED.titleResId).isEqualTo(Resources.ID_NULL) + if (SdkLevel.isAtLeastU()) { + assertThat(COLLAPSIBLE_BAREBONE.titleResId).isEqualTo(REFERENCE_RES_ID) + assertThat(COLLAPSIBLE_ALL_OPTIONAL.titleResId).isEqualTo(REFERENCE_RES_ID) + assertThat(RIGID_BAREBONE.titleResId).isEqualTo(REFERENCE_RES_ID) + assertThat(RIGID_ALL_OPTIONAL.titleResId).isEqualTo(REFERENCE_RES_ID) + assertThat(HIDDEN_BAREBONE.titleResId).isEqualTo(Resources.ID_NULL) + assertThat(HIDDEN_ALL_OPTIONAL.titleResId).isEqualTo(REFERENCE_RES_ID) + } } @Test fun getSummaryResId_returnsSummaryResId() { - assertThat(COLLAPSIBLE_WITH_SUMMARY.summaryResId).isEqualTo(REFERENCE_RES_ID) - assertThat(COLLAPSIBLE_WITH_ICON.summaryResId).isEqualTo(Resources.ID_NULL) - assertThat(COLLAPSIBLE_WITH_BOTH.summaryResId).isEqualTo(REFERENCE_RES_ID) - assertThat(RIGID.summaryResId).isEqualTo(Resources.ID_NULL) + assertThat(COLLAPSIBLE_INFERRED_WITH_SUMMARY.summaryResId).isEqualTo(REFERENCE_RES_ID) + assertThat(COLLAPSIBLE_INFERRED_WITH_ICON.summaryResId).isEqualTo(Resources.ID_NULL) + assertThat(COLLAPSIBLE_INFERRED_WITH_BOTH.summaryResId).isEqualTo(REFERENCE_RES_ID) + assertThat(RIGID_INFERRED.summaryResId).isEqualTo(Resources.ID_NULL) // This is not an enforced invariant, summaryResId should just be ignored for hidden groups - assertThat(HIDDEN.summaryResId).isEqualTo(Resources.ID_NULL) + assertThat(HIDDEN_INFERRED.summaryResId).isEqualTo(Resources.ID_NULL) + if (SdkLevel.isAtLeastU()) { + assertThat(COLLAPSIBLE_BAREBONE.titleResId).isEqualTo(REFERENCE_RES_ID) + assertThat(COLLAPSIBLE_ALL_OPTIONAL.titleResId).isEqualTo(REFERENCE_RES_ID) + assertThat(RIGID_BAREBONE.titleResId).isEqualTo(REFERENCE_RES_ID) + assertThat(RIGID_ALL_OPTIONAL.titleResId).isEqualTo(REFERENCE_RES_ID) + assertThat(HIDDEN_BAREBONE.titleResId).isEqualTo(Resources.ID_NULL) + assertThat(HIDDEN_ALL_OPTIONAL.titleResId).isEqualTo(REFERENCE_RES_ID) + } } @Test fun getStatelessIconType_returnsStatelessIconType() { - assertThat(COLLAPSIBLE_WITH_SUMMARY.statelessIconType) + assertThat(COLLAPSIBLE_INFERRED_WITH_SUMMARY.statelessIconType) .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_NONE) - assertThat(COLLAPSIBLE_WITH_ICON.statelessIconType) + assertThat(COLLAPSIBLE_INFERRED_WITH_ICON.statelessIconType) .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) - assertThat(COLLAPSIBLE_WITH_BOTH.statelessIconType) + assertThat(COLLAPSIBLE_INFERRED_WITH_BOTH.statelessIconType) .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) - assertThat(RIGID.statelessIconType).isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_NONE) + assertThat(RIGID_INFERRED.statelessIconType) + .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_NONE) // This is not an enforced invariant // statelessIconType should just be ignored for hidden groups - assertThat(HIDDEN.statelessIconType).isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_NONE) + assertThat(HIDDEN_INFERRED.statelessIconType) + .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_NONE) + if (SdkLevel.isAtLeastU()) { + assertThat(COLLAPSIBLE_BAREBONE.statelessIconType) + .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_NONE) + assertThat(COLLAPSIBLE_ALL_OPTIONAL.statelessIconType) + .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) + assertThat(RIGID_BAREBONE.statelessIconType) + .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_NONE) + assertThat(RIGID_ALL_OPTIONAL.statelessIconType) + .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) + assertThat(HIDDEN_BAREBONE.statelessIconType) + .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_NONE) + assertThat(HIDDEN_ALL_OPTIONAL.statelessIconType) + .isEqualTo(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) + } } @Test fun getSafetySources_returnsSafetySources() { - assertThat(COLLAPSIBLE_WITH_SUMMARY.safetySources) + assertThat(COLLAPSIBLE_INFERRED_WITH_SUMMARY.safetySources) .containsExactly(SafetySourceTest.DYNAMIC_BAREBONE) - assertThat(COLLAPSIBLE_WITH_ICON.safetySources) + assertThat(COLLAPSIBLE_INFERRED_WITH_ICON.safetySources) .containsExactly(SafetySourceTest.STATIC_BAREBONE) - assertThat(COLLAPSIBLE_WITH_BOTH.safetySources) + assertThat(COLLAPSIBLE_INFERRED_WITH_BOTH.safetySources) .containsExactly( SafetySourceTest.DYNAMIC_BAREBONE, SafetySourceTest.STATIC_BAREBONE, SafetySourceTest.ISSUE_ONLY_BAREBONE) .inOrder() - assertThat(RIGID.safetySources).containsExactly(SafetySourceTest.STATIC_BAREBONE) - assertThat(HIDDEN.safetySources).containsExactly(SafetySourceTest.ISSUE_ONLY_BAREBONE) + assertThat(RIGID_INFERRED.safetySources).containsExactly(SafetySourceTest.STATIC_BAREBONE) + assertThat(HIDDEN_INFERRED.safetySources) + .containsExactly(SafetySourceTest.ISSUE_ONLY_BAREBONE) + if (SdkLevel.isAtLeastU()) { + assertThat(COLLAPSIBLE_BAREBONE.safetySources) + .containsExactly(SafetySourceTest.DYNAMIC_BAREBONE) + assertThat(COLLAPSIBLE_ALL_OPTIONAL.safetySources) + .containsExactly( + SafetySourceTest.DYNAMIC_BAREBONE, + SafetySourceTest.STATIC_BAREBONE, + SafetySourceTest.ISSUE_ONLY_BAREBONE) + assertThat(RIGID_BAREBONE.safetySources) + .containsExactly(SafetySourceTest.STATIC_BAREBONE) + assertThat(RIGID_ALL_OPTIONAL.safetySources) + .containsExactly( + SafetySourceTest.DYNAMIC_BAREBONE, + SafetySourceTest.STATIC_BAREBONE, + SafetySourceTest.ISSUE_ONLY_BAREBONE) + assertThat(HIDDEN_BAREBONE.safetySources) + .containsExactly(SafetySourceTest.ISSUE_ONLY_BAREBONE) + assertThat(HIDDEN_ALL_OPTIONAL.safetySources) + .containsExactly(SafetySourceTest.ISSUE_ONLY_BAREBONE) + } } @Test fun getSafetySources_mutationsAreNotAllowed() { - val sources = COLLAPSIBLE_WITH_SUMMARY.safetySources + val sources = COLLAPSIBLE_INFERRED_WITH_SUMMARY.safetySources assertFailsWith(UnsupportedOperationException::class) { sources.add(SafetySourceTest.DYNAMIC_BAREBONE) @@ -114,7 +196,7 @@ class SafetySourcesGroupTest { fun builder_addSafetySource_doesNotMutatePreviouslyBuiltInstance() { val safetySourcesGroupBuilder = SafetySourcesGroup.Builder() - .setId(COLLAPSIBLE_WITH_SUMMARY_ID) + .setId(COLLAPSIBLE_INFERRED_WITH_SUMMARY_ID) .setTitleResId(REFERENCE_RES_ID) .setSummaryResId(REFERENCE_RES_ID) .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE) @@ -126,41 +208,134 @@ class SafetySourcesGroupTest { } @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun build_hiddenGroupWithDynamicSource_throwsIllegalStateException() { + val builder = + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN) + .setId(HIDDEN_BAREBONE_ID) + .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE) + + val exception = assertFailsWith(IllegalStateException::class) { builder.build() } + assertThat(exception) + .hasMessageThat() + .isEqualTo( + "Safety sources groups of type hidden can only contain sources of type issue-only") + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun build_hiddenGroupWithStaticSource_throwsIllegalStateException() { + val builder = + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN) + .setId(HIDDEN_BAREBONE_ID) + .addSafetySource(SafetySourceTest.STATIC_BAREBONE) + + val exception = assertFailsWith(IllegalStateException::class) { builder.build() } + assertThat(exception) + .hasMessageThat() + .isEqualTo( + "Safety sources groups of type hidden can only contain sources of type issue-only") + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun build_collapsibleGroupWithIssueOnlySource_throwsIllegalStateException() { + val builder = + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE) + .setId(COLLAPSIBLE_BAREBONE_ID) + .setTitleResId(REFERENCE_RES_ID) + .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE) + + val exception = assertFailsWith(IllegalStateException::class) { builder.build() } + assertThat(exception) + .hasMessageThat() + .isEqualTo( + "Safety sources groups containing only sources of type issue-only must be of " + + "type hidden") + } + + @Test + @SdkSuppress(minSdkVersion = UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") + fun build_rigidGroupWithIssueOnlySource_throwsIllegalStateException() { + val builder = + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_RIGID) + .setId(RIGID_BAREBONE_ID) + .setTitleResId(REFERENCE_RES_ID) + .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE) + + val exception = assertFailsWith(IllegalStateException::class) { builder.build() } + assertThat(exception) + .hasMessageThat() + .isEqualTo( + "Safety sources groups containing only sources of type issue-only must be of " + + "type hidden") + } + + @Test fun describeContents_returns0() { - assertThat(COLLAPSIBLE_WITH_SUMMARY.describeContents()).isEqualTo(0) - assertThat(COLLAPSIBLE_WITH_ICON.describeContents()).isEqualTo(0) - assertThat(COLLAPSIBLE_WITH_BOTH.describeContents()).isEqualTo(0) - assertThat(RIGID.describeContents()).isEqualTo(0) - assertThat(HIDDEN.describeContents()).isEqualTo(0) + assertThat(COLLAPSIBLE_INFERRED_WITH_SUMMARY.describeContents()).isEqualTo(0) + assertThat(COLLAPSIBLE_INFERRED_WITH_ICON.describeContents()).isEqualTo(0) + assertThat(COLLAPSIBLE_INFERRED_WITH_BOTH.describeContents()).isEqualTo(0) + assertThat(RIGID_INFERRED.describeContents()).isEqualTo(0) + assertThat(HIDDEN_INFERRED.describeContents()).isEqualTo(0) + if (SdkLevel.isAtLeastU()) { + assertThat(COLLAPSIBLE_BAREBONE.describeContents()).isEqualTo(0) + assertThat(COLLAPSIBLE_ALL_OPTIONAL.describeContents()).isEqualTo(0) + assertThat(RIGID_BAREBONE.describeContents()).isEqualTo(0) + assertThat(RIGID_ALL_OPTIONAL.describeContents()).isEqualTo(0) + assertThat(HIDDEN_BAREBONE.describeContents()).isEqualTo(0) + assertThat(HIDDEN_ALL_OPTIONAL.describeContents()).isEqualTo(0) + } } @Test fun parcelRoundTrip_recreatesEqual() { - assertThat(COLLAPSIBLE_WITH_SUMMARY).recreatesEqual(SafetySourcesGroup.CREATOR) - assertThat(COLLAPSIBLE_WITH_ICON).recreatesEqual(SafetySourcesGroup.CREATOR) - assertThat(COLLAPSIBLE_WITH_BOTH).recreatesEqual(SafetySourcesGroup.CREATOR) - assertThat(RIGID).recreatesEqual(SafetySourcesGroup.CREATOR) - assertThat(HIDDEN).recreatesEqual(SafetySourcesGroup.CREATOR) + assertThat(COLLAPSIBLE_INFERRED_WITH_SUMMARY).recreatesEqual(SafetySourcesGroup.CREATOR) + assertThat(COLLAPSIBLE_INFERRED_WITH_ICON).recreatesEqual(SafetySourcesGroup.CREATOR) + assertThat(COLLAPSIBLE_INFERRED_WITH_BOTH).recreatesEqual(SafetySourcesGroup.CREATOR) + assertThat(RIGID_INFERRED).recreatesEqual(SafetySourcesGroup.CREATOR) + assertThat(HIDDEN_INFERRED).recreatesEqual(SafetySourcesGroup.CREATOR) + if (SdkLevel.isAtLeastU()) { + assertThat(COLLAPSIBLE_BAREBONE).recreatesEqual(SafetySourcesGroup.CREATOR) + assertThat(COLLAPSIBLE_ALL_OPTIONAL).recreatesEqual(SafetySourcesGroup.CREATOR) + assertThat(RIGID_BAREBONE).recreatesEqual(SafetySourcesGroup.CREATOR) + assertThat(RIGID_ALL_OPTIONAL).recreatesEqual(SafetySourcesGroup.CREATOR) + assertThat(HIDDEN_BAREBONE).recreatesEqual(SafetySourcesGroup.CREATOR) + assertThat(HIDDEN_ALL_OPTIONAL).recreatesEqual(SafetySourcesGroup.CREATOR) + } } @Test fun equalsHashCodeToString_usingEqualsHashCodeToStringTester() { EqualsHashCodeToStringTester() - .addEqualityGroup(COLLAPSIBLE_WITH_SUMMARY) - .addEqualityGroup(COLLAPSIBLE_WITH_ICON) + .addEqualityGroup(COLLAPSIBLE_INFERRED_WITH_SUMMARY) + .addEqualityGroup(COLLAPSIBLE_INFERRED_WITH_ICON) .addEqualityGroup( - COLLAPSIBLE_WITH_BOTH, - SafetySourcesGroup.Builder() - .setId(COLLAPSIBLE_WITH_BOTH_ID) - .setTitleResId(REFERENCE_RES_ID) - .setSummaryResId(REFERENCE_RES_ID) - .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) - .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE) - .addSafetySource(SafetySourceTest.STATIC_BAREBONE) - .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE) - .build()) - .addEqualityGroup(RIGID) - .addEqualityGroup(HIDDEN) + *mutableListOf( + COLLAPSIBLE_INFERRED_WITH_BOTH, + SafetySourcesGroup.Builder() + .setId(COLLAPSIBLE_INFERRED_WITH_BOTH_ID) + .setTitleResId(REFERENCE_RES_ID) + .setSummaryResId(REFERENCE_RES_ID) + .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) + .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE) + .addSafetySource(SafetySourceTest.STATIC_BAREBONE) + .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE) + .build()) + .apply { if (SdkLevel.isAtLeastU()) add(COLLAPSIBLE_ALL_OPTIONAL) } + .toTypedArray()) + .addEqualityGroup( + *mutableListOf(RIGID_INFERRED) + .apply { if (SdkLevel.isAtLeastU()) add(RIGID_BAREBONE) } + .toTypedArray()) + .addEqualityGroup( + *mutableListOf(HIDDEN_INFERRED) + .apply { if (SdkLevel.isAtLeastU()) add(HIDDEN_BAREBONE) } + .toTypedArray()) .addEqualityGroup( SafetySourcesGroup.Builder() .setId("other") @@ -171,7 +346,7 @@ class SafetySourcesGroupTest { .build()) .addEqualityGroup( SafetySourcesGroup.Builder() - .setId(COLLAPSIBLE_WITH_BOTH_ID) + .setId(COLLAPSIBLE_INFERRED_WITH_BOTH_ID) .setTitleResId(-1) .setSummaryResId(REFERENCE_RES_ID) .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) @@ -179,7 +354,7 @@ class SafetySourcesGroupTest { .build()) .addEqualityGroup( SafetySourcesGroup.Builder() - .setId(COLLAPSIBLE_WITH_BOTH_ID) + .setId(COLLAPSIBLE_INFERRED_WITH_BOTH_ID) .setTitleResId(REFERENCE_RES_ID) .setSummaryResId(-1) .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) @@ -187,7 +362,7 @@ class SafetySourcesGroupTest { .build()) .addEqualityGroup( SafetySourcesGroup.Builder() - .setId(COLLAPSIBLE_WITH_BOTH_ID) + .setId(COLLAPSIBLE_INFERRED_WITH_BOTH_ID) .setTitleResId(REFERENCE_RES_ID) .setSummaryResId(REFERENCE_RES_ID) .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_NONE) @@ -195,44 +370,57 @@ class SafetySourcesGroupTest { .build()) .addEqualityGroup( SafetySourcesGroup.Builder() - .setId(COLLAPSIBLE_WITH_BOTH_ID) + .setId(COLLAPSIBLE_INFERRED_WITH_BOTH_ID) .setTitleResId(REFERENCE_RES_ID) .setSummaryResId(REFERENCE_RES_ID) .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) .addSafetySource(SafetySourceTest.STATIC_BAREBONE) .build()) + .apply { + if (SdkLevel.isAtLeastU()) { + addEqualityGroup(COLLAPSIBLE_BAREBONE) + addEqualityGroup(RIGID_ALL_OPTIONAL) + addEqualityGroup(HIDDEN_ALL_OPTIONAL) + } + } .test() } companion object { private const val REFERENCE_RES_ID = 9999 - private const val COLLAPSIBLE_WITH_SUMMARY_ID = "collapsible_with_summary" - private const val COLLAPSIBLE_WITH_ICON_ID = "collapsible_with_icon" - private const val COLLAPSIBLE_WITH_BOTH_ID = "collapsible_with_both" - private const val RIGID_ID = "rigid" - private const val HIDDEN_ID = "hidden" + private const val COLLAPSIBLE_BAREBONE_ID = "collapsible_barebone" + private const val COLLAPSIBLE_ALL_OPTIONAL_ID = "collapsible_all_optional" + private const val RIGID_BAREBONE_ID = "rigid_barebone" + private const val RIGID_ALL_OPTIONAL_ID = "rigid_all_optional" + private const val HIDDEN_BAREBONE_ID = "hidden_barebone" + private const val HIDDEN_ALL_OPTIONAL_ID = "hidden_all_optional" + private const val COLLAPSIBLE_INFERRED_WITH_SUMMARY_ID = "collapsible_inferred_with_summary" + private const val COLLAPSIBLE_INFERRED_WITH_ICON_ID = "collapsible_inferred_with_icon" + private const val COLLAPSIBLE_INFERRED_WITH_BOTH_ID = COLLAPSIBLE_ALL_OPTIONAL_ID + private const val RIGID_INFERRED_ID = RIGID_BAREBONE_ID + private const val HIDDEN_INFERRED_ID = HIDDEN_BAREBONE_ID // TODO(b/230078826): Consider extracting shared constants to a separate file. - internal val COLLAPSIBLE_WITH_SUMMARY = + internal val COLLAPSIBLE_INFERRED_WITH_SUMMARY = SafetySourcesGroup.Builder() - .setId(COLLAPSIBLE_WITH_SUMMARY_ID) + .setId(COLLAPSIBLE_INFERRED_WITH_SUMMARY_ID) .setTitleResId(REFERENCE_RES_ID) .setSummaryResId(REFERENCE_RES_ID) .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE) .build() - private val COLLAPSIBLE_WITH_ICON = + private val COLLAPSIBLE_INFERRED_WITH_ICON = SafetySourcesGroup.Builder() - .setId(COLLAPSIBLE_WITH_ICON_ID) + .setId(COLLAPSIBLE_INFERRED_WITH_ICON_ID) .setTitleResId(REFERENCE_RES_ID) .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) .addSafetySource(SafetySourceTest.STATIC_BAREBONE) .build() - private val COLLAPSIBLE_WITH_BOTH = + private val COLLAPSIBLE_INFERRED_WITH_BOTH = SafetySourcesGroup.Builder() - .setId(COLLAPSIBLE_WITH_BOTH_ID) + .setId(COLLAPSIBLE_INFERRED_WITH_BOTH_ID) .setTitleResId(REFERENCE_RES_ID) .setSummaryResId(REFERENCE_RES_ID) .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) @@ -241,17 +429,86 @@ class SafetySourcesGroupTest { .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE) .build() - internal val RIGID = + private val COLLAPSIBLE_BAREBONE: SafetySourcesGroup + @RequiresApi(UPSIDE_DOWN_CAKE) + get() = + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE) + .setId(COLLAPSIBLE_BAREBONE_ID) + .setTitleResId(REFERENCE_RES_ID) + .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE) + .build() + + private val COLLAPSIBLE_ALL_OPTIONAL: SafetySourcesGroup + @RequiresApi(UPSIDE_DOWN_CAKE) + get() = + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_COLLAPSIBLE) + .setId(COLLAPSIBLE_ALL_OPTIONAL_ID) + .setTitleResId(REFERENCE_RES_ID) + .setSummaryResId(REFERENCE_RES_ID) + .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) + .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE) + .addSafetySource(SafetySourceTest.STATIC_BAREBONE) + .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE) + .build() + + internal val RIGID_INFERRED = SafetySourcesGroup.Builder() - .setId(RIGID_ID) + .setId(RIGID_INFERRED_ID) .setTitleResId(REFERENCE_RES_ID) .addSafetySource(SafetySourceTest.STATIC_BAREBONE) .build() - internal val HIDDEN = + private val RIGID_BAREBONE: SafetySourcesGroup + @RequiresApi(UPSIDE_DOWN_CAKE) + get() = + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_RIGID) + .setId(RIGID_BAREBONE_ID) + .setTitleResId(REFERENCE_RES_ID) + .addSafetySource(SafetySourceTest.STATIC_BAREBONE) + .build() + + private val RIGID_ALL_OPTIONAL: SafetySourcesGroup + @RequiresApi(UPSIDE_DOWN_CAKE) + get() = + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_RIGID) + .setId(RIGID_ALL_OPTIONAL_ID) + .setTitleResId(REFERENCE_RES_ID) + .setSummaryResId(REFERENCE_RES_ID) + .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) + .addSafetySource(SafetySourceTest.DYNAMIC_BAREBONE) + .addSafetySource(SafetySourceTest.STATIC_BAREBONE) + .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE) + .build() + + internal val HIDDEN_INFERRED = SafetySourcesGroup.Builder() - .setId(HIDDEN_ID) + .setId(HIDDEN_INFERRED_ID) .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE) .build() + + private val HIDDEN_BAREBONE: SafetySourcesGroup + @RequiresApi(UPSIDE_DOWN_CAKE) + get() = + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN) + .setId(HIDDEN_BAREBONE_ID) + .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE) + .build() + + private val HIDDEN_ALL_OPTIONAL: SafetySourcesGroup + @RequiresApi(UPSIDE_DOWN_CAKE) + get() = + SafetySourcesGroup.Builder() + .setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN) + .setId(HIDDEN_ALL_OPTIONAL_ID) + .setTitleResId(REFERENCE_RES_ID) + .setSummaryResId(REFERENCE_RES_ID) + .setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) + .addSafetySource(SafetySourceTest.ISSUE_ONLY_BAREBONE) + .build() } } diff --git a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterCtsConfigs.kt b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterCtsConfigs.kt index fce6cdad7..56a095ba8 100644 --- a/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterCtsConfigs.kt +++ b/tests/cts/safetycenter/src/android/safetycenter/cts/testing/SafetyCenterCtsConfigs.kt @@ -553,7 +553,14 @@ object SafetyCenterCtsConfigs { .build()) .addSafetySourcesGroup( safetySourcesGroupBuilder(STATIC_GROUP_ID) - .setSummaryResId(Resources.ID_NULL) + .apply { + if (SdkLevel.isAtLeastU()) { + setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_RIGID) + setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) + } else { + setSummaryResId(Resources.ID_NULL) + } + } .addSafetySource( staticSafetySourceBuilder(STATIC_BAREBONE_ID) .setSummaryResId(Resources.ID_NULL) @@ -566,6 +573,14 @@ object SafetyCenterCtsConfigs { .addSafetySourcesGroup( SafetySourcesGroup.Builder() .setId(ISSUE_ONLY_GROUP_ID) + .apply { + if (SdkLevel.isAtLeastU()) { + setType(SafetySourcesGroup.SAFETY_SOURCES_GROUP_TYPE_HIDDEN) + setTitleResId(android.R.string.ok) + setSummaryResId(android.R.string.ok) + setStatelessIconType(SafetySourcesGroup.STATELESS_ICON_TYPE_PRIVACY) + } + } .addSafetySource( issueOnlySafetySourceBuilder(ISSUE_ONLY_BAREBONE_ID) .setRefreshOnPageOpenAllowed(false) |