/* * Copyright (C) 2022 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. */ package android.safetycenter.config; import static android.os.Build.VERSION_CODES.TIRAMISU; import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; import static android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM; import static java.util.Objects.requireNonNull; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringRes; import android.annotation.SystemApi; import android.content.res.Resources; import android.os.Parcel; import android.os.Parcelable; import android.util.ArraySet; import androidx.annotation.RequiresApi; import com.android.modules.utils.build.SdkLevel; import com.android.permission.flags.Flags; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; import java.util.Objects; import java.util.Set; /** * Data class used to represent the initial configuration of a safety source. * * @hide */ @SystemApi @RequiresApi(TIRAMISU) public final class SafetySource implements Parcelable { /** * Static safety source. * *
A static safety source is a source completely defined in the Safety Center configuration. * The source is displayed with no icon and neither the description displayed nor the tap * behavior can be changed at runtime. A static safety source cannot have any issue associated * with it. */ public static final int SAFETY_SOURCE_TYPE_STATIC = 1; /** * Dynamic safety source. * *
The status, description, tap behavior, and related issues of a dynamic safety source can * be set at runtime by the package that owns the source. The source is displayed with an icon * reflecting the status when part of a collapsible safety sources group. */ public static final int SAFETY_SOURCE_TYPE_DYNAMIC = 2; /** * Issue-only safety source. * *
An issue-only safety source is not displayed as an entry in the Safety Center page. The
* package that owns an issue-only safety source can set the list of issues associated with the
* source at runtime.
*/
public static final int SAFETY_SOURCE_TYPE_ISSUE_ONLY = 3;
/**
* All possible safety source types.
*
* @hide
*/
@IntDef(
prefix = {"SAFETY_SOURCE_TYPE_"},
value = {
SAFETY_SOURCE_TYPE_STATIC,
SAFETY_SOURCE_TYPE_DYNAMIC,
SAFETY_SOURCE_TYPE_ISSUE_ONLY
})
@Retention(RetentionPolicy.SOURCE)
public @interface SafetySourceType {}
/** Profile property unspecified. */
public static final int PROFILE_NONE = 0;
/**
* Even when the active user has managed enabled profiles, a visible safety source will be
* displayed as a single entry for the primary profile. For dynamic sources, refresh requests
* will be sent to and set requests will be accepted from the primary profile only.
*/
public static final int PROFILE_PRIMARY = 1;
/**
* When the user has managed enabled profiles, a visible safety source will be displayed as
* multiple entries one for each enabled profile. For dynamic sources, refresh requests will be
* sent to and set requests will be accepted from all profiles.
*/
public static final int PROFILE_ALL = 2;
/**
* All possible profile configurations for a safety source.
*
* @hide
*/
@IntDef(
prefix = {"PROFILE_"},
value = {PROFILE_NONE, PROFILE_PRIMARY, PROFILE_ALL})
@Retention(RetentionPolicy.SOURCE)
public @interface Profile {}
/**
* The dynamic safety source will create an enabled entry in the Safety Center page until a set
* request is received.
*/
public static final int INITIAL_DISPLAY_STATE_ENABLED = 0;
/**
* The dynamic safety source will create a disabled entry in the Safety Center page until a set
* request is received.
*/
public static final int INITIAL_DISPLAY_STATE_DISABLED = 1;
/**
* The dynamic safety source will have no entry in the Safety Center page until a set request is
* received.
*/
public static final int INITIAL_DISPLAY_STATE_HIDDEN = 2;
/**
* All possible initial display states for a dynamic safety source.
*
* @hide
*/
@IntDef(
prefix = {"INITIAL_DISPLAY_STATE_"},
value = {
INITIAL_DISPLAY_STATE_ENABLED,
INITIAL_DISPLAY_STATE_DISABLED,
INITIAL_DISPLAY_STATE_HIDDEN
})
@Retention(RetentionPolicy.SOURCE)
public @interface InitialDisplayState {}
@NonNull
public static final Creator The id is unique among safety sources in a Safety Center configuration.
*/
@NonNull
public String getId() {
return mId;
}
/**
* Returns the package name of this safety source.
*
* This is the package that owns the source. The package will receive refresh requests, and
* it can send set requests for the source. The package is also used to create an explicit
* pending intent from the intent action in the package context.
*
* @throws UnsupportedOperationException if the source is of type {@link
* SafetySource#SAFETY_SOURCE_TYPE_STATIC} even if the optional package name field for the
* source is set, for sources of type {@link SafetySource#SAFETY_SOURCE_TYPE_STATIC} use
* {@link SafetySource#getOptionalPackageName()}
*/
@NonNull
public String getPackageName() {
if (mType == SAFETY_SOURCE_TYPE_STATIC) {
throw new UnsupportedOperationException(
"getPackageName unsupported for static safety source");
}
return mPackageName;
}
/**
* Returns the package name of this safety source or null if undefined.
*
* This is the package that owns the source.
*
* The package is always defined for sources of type dynamic and issue-only. The package will
* receive refresh requests, and it can send set requests for sources of type dynamic and
* issue-only. The package is also used to create an explicit pending intent in the package
* context from the intent action if defined.
*
* The package is optional for sources of type static. If present, the package is used to
* create an explicit pending intent in the package context from the intent action.
*/
@Nullable
@RequiresApi(UPSIDE_DOWN_CAKE)
public String getOptionalPackageName() {
if (!SdkLevel.isAtLeastU()) {
throw new UnsupportedOperationException(
"Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
return mPackageName;
}
/**
* Returns the resource id of the title of this safety source.
*
* The id refers to a string resource that is either accessible from any resource context or
* that is accessible from the same resource context that was used to load the Safety Center
* configuration. The id is {@link Resources#ID_NULL} when a title is not provided.
*
* @throws UnsupportedOperationException if the source is of type {@link
* SafetySource#SAFETY_SOURCE_TYPE_ISSUE_ONLY}
*/
@StringRes
public int getTitleResId() {
if (mType == SAFETY_SOURCE_TYPE_ISSUE_ONLY) {
throw new UnsupportedOperationException(
"getTitleResId unsupported for issue-only safety source");
}
return mTitleResId;
}
/**
* Returns the resource id of the title for work of this safety source.
*
* The id refers to a string resource that is either accessible from any resource context or
* that is accessible from the same resource context that was used to load the Safety Center
* configuration. The id is {@link Resources#ID_NULL} when a title for work is not provided.
*
* @throws UnsupportedOperationException if the source is of type {@link
* SafetySource#SAFETY_SOURCE_TYPE_ISSUE_ONLY} or if the profile property of the source is
* set to {@link SafetySource#PROFILE_PRIMARY}
*/
@StringRes
public int getTitleForWorkResId() {
if (mType == SAFETY_SOURCE_TYPE_ISSUE_ONLY) {
throw new UnsupportedOperationException(
"getTitleForWorkResId unsupported for issue-only safety source");
}
if (mProfile == PROFILE_PRIMARY) {
throw new UnsupportedOperationException(
"getTitleForWorkResId unsupported for primary profile safety source");
}
return mTitleForWorkResId;
}
/**
* Returns the resource id of the title for private profile of this safety source.
*
* The id refers to a string resource that is either accessible from any resource context or
* that is accessible from the same resource context that was used to load the Safety Center
* configuration. The id is {@link Resources#ID_NULL} when a title for private profile is not
* provided.
*
* @throws UnsupportedOperationException if the source is of type {@link
* SafetySource#SAFETY_SOURCE_TYPE_ISSUE_ONLY} or if the profile property of the source is
* set to {@link SafetySource#PROFILE_PRIMARY}
*/
@FlaggedApi(Flags.FLAG_PRIVATE_PROFILE_TITLE_API)
@RequiresApi(VANILLA_ICE_CREAM)
@StringRes
public int getTitleForPrivateProfileResId() {
if (!SdkLevel.isAtLeastV()) {
throw new UnsupportedOperationException(
"getTitleForPrivateProfileResId unsupported for SDKs lower than V");
}
if (mType == SAFETY_SOURCE_TYPE_ISSUE_ONLY) {
throw new UnsupportedOperationException(
"getTitleForPrivateProfileResId unsupported for issue-only safety source");
}
if (mProfile == PROFILE_PRIMARY) {
throw new UnsupportedOperationException(
"getTitleForPrivateProfileResId unsupported for primary profile safety source");
}
return mTitleForPrivateProfileResId;
}
/**
* Returns the resource id of the summary of this safety source.
*
* The id refers to a string resource that is either accessible from any resource context or
* that is accessible from the same resource context that was used to load the Safety Center
* configuration. The id is {@link Resources#ID_NULL} when a summary is not provided.
*
* @throws UnsupportedOperationException if the source is of type {@link
* SafetySource#SAFETY_SOURCE_TYPE_ISSUE_ONLY}
*/
@StringRes
public int getSummaryResId() {
if (mType == SAFETY_SOURCE_TYPE_ISSUE_ONLY) {
throw new UnsupportedOperationException(
"getSummaryResId unsupported for issue-only safety source");
}
return mSummaryResId;
}
/**
* Returns the intent action of this safety source.
*
* An intent created from the intent action should resolve to a public activity. If the
* source is displayed as an entry in the Safety Center page, and if the action is set to {@code
* null} or if it does not resolve to an activity the source will be marked as disabled.
*
* @throws UnsupportedOperationException if the source is of type {@link
* SafetySource#SAFETY_SOURCE_TYPE_ISSUE_ONLY}
*/
@Nullable
public String getIntentAction() {
if (mType == SAFETY_SOURCE_TYPE_ISSUE_ONLY) {
throw new UnsupportedOperationException(
"getIntentAction unsupported for issue-only safety source");
}
return mIntentAction;
}
/** Returns the profile property of this safety source. */
@Profile
public int getProfile() {
return mProfile;
}
/**
* Returns the initial display state of this safety source.
*
* @throws UnsupportedOperationException if the source is of type {@link
* SafetySource#SAFETY_SOURCE_TYPE_STATIC} or {@link
* SafetySource#SAFETY_SOURCE_TYPE_ISSUE_ONLY}
*/
@InitialDisplayState
public int getInitialDisplayState() {
if (mType == SAFETY_SOURCE_TYPE_STATIC) {
throw new UnsupportedOperationException(
"getInitialDisplayState unsupported for static safety source");
}
if (mType == SAFETY_SOURCE_TYPE_ISSUE_ONLY) {
throw new UnsupportedOperationException(
"getInitialDisplayState unsupported for issue-only safety source");
}
return mInitialDisplayState;
}
/**
* Returns the maximum severity level of this safety source.
*
* The maximum severity level dictates the maximum severity level values that can be used in
* the source status or the source issues when setting the source data at runtime. A source can
* always send a status severity level of at least {@link
* android.safetycenter.SafetySourceData#SEVERITY_LEVEL_INFORMATION} even if the maximum
* severity level is set to a lower value.
*
* @throws UnsupportedOperationException if the source is of type {@link
* SafetySource#SAFETY_SOURCE_TYPE_STATIC}
*/
public int getMaxSeverityLevel() {
if (mType == SAFETY_SOURCE_TYPE_STATIC) {
throw new UnsupportedOperationException(
"getMaxSeverityLevel unsupported for static safety source");
}
return mMaxSeverityLevel;
}
/**
* Returns the resource id of the search terms of this safety source.
*
* The id refers to a string resource that is either accessible from any resource context or
* that is accessible from the same resource context that was used to load the Safety Center
* configuration. The id is {@link Resources#ID_NULL} when search terms are not provided.
*
* @throws UnsupportedOperationException if the source is of type {@link
* SafetySource#SAFETY_SOURCE_TYPE_ISSUE_ONLY}
*/
@StringRes
public int getSearchTermsResId() {
if (mType == SAFETY_SOURCE_TYPE_ISSUE_ONLY) {
throw new UnsupportedOperationException(
"getSearchTermsResId unsupported for issue-only safety source");
}
return mSearchTermsResId;
}
/**
* Returns the logging allowed property of this safety source.
*
* @throws UnsupportedOperationException if the source is of type {@link
* SafetySource#SAFETY_SOURCE_TYPE_STATIC}
*/
public boolean isLoggingAllowed() {
if (mType == SAFETY_SOURCE_TYPE_STATIC) {
throw new UnsupportedOperationException(
"isLoggingAllowed unsupported for static safety source");
}
return mLoggingAllowed;
}
/**
* Returns the refresh on page open allowed property of this safety source.
*
* If set to {@code true}, a refresh request will be sent to the source when the Safety
* Center page is opened.
*
* @throws UnsupportedOperationException if the source is of type {@link
* SafetySource#SAFETY_SOURCE_TYPE_STATIC}
*/
public boolean isRefreshOnPageOpenAllowed() {
if (mType == SAFETY_SOURCE_TYPE_STATIC) {
throw new UnsupportedOperationException(
"isRefreshOnPageOpenAllowed unsupported for static safety source");
}
return mRefreshOnPageOpenAllowed;
}
/**
* Returns whether Safety Center may post Notifications about issues reported by this {@link
* SafetySource}.
*
* @see Builder#setNotificationsAllowed(boolean)
*/
@RequiresApi(UPSIDE_DOWN_CAKE)
public boolean areNotificationsAllowed() {
if (!SdkLevel.isAtLeastU()) {
throw new UnsupportedOperationException(
"Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
return mNotificationsAllowed;
}
/**
* Returns the deduplication group this source belongs to.
*
* Sources which are part of the same deduplication group can coordinate to deduplicate their
* issues.
*/
@Nullable
@RequiresApi(UPSIDE_DOWN_CAKE)
public String getDeduplicationGroup() {
if (!SdkLevel.isAtLeastU()) {
throw new UnsupportedOperationException(
"Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
return mDeduplicationGroup;
}
/**
* Returns a set of package certificate hashes representing valid signed packages that represent
* this {@link SafetySource}.
*
* If one or more certificate hashes are set, Safety Center will validate that a package
* calling {@link android.safetycenter.SafetyCenterManager#setSafetySourceData} is signed with
* one of the certificates provided.
*
* The default value is an empty {@code Set}, in which case only the package name is
* validated.
*
* @see Builder#addPackageCertificateHash(String)
*/
@NonNull
@RequiresApi(UPSIDE_DOWN_CAKE)
public Set The id must be unique among safety sources in a Safety Center configuration.
*/
@NonNull
public Builder setId(@Nullable String id) {
mId = id;
return this;
}
/**
* Sets the package name of this safety source.
*
* This is the package that owns the source. The package will receive refresh requests
* and it can send set requests for the source.
*
* The package name is required for sources of type dynamic and issue-only. The package
* name is prohibited for sources of type static.
*/
@NonNull
public Builder setPackageName(@Nullable String packageName) {
mPackageName = packageName;
return this;
}
/**
* Sets the resource id of the title of this safety source.
*
* The id must refer to a string resource that is either accessible from any resource
* context or that is accessible from the same resource context that was used to load the
* Safety Center config. The id defaults to {@link Resources#ID_NULL} when a title is not
* provided.
*
* The title is required for sources of type static and for sources of type dynamic that
* are not hidden and that do not provide search terms. The title is prohibited for sources
* of type issue-only.
*/
@NonNull
public Builder setTitleResId(@StringRes int titleResId) {
mTitleResId = titleResId;
return this;
}
/**
* Sets the resource id of the title for work of this safety source.
*
* The id must refer to a string resource that is either accessible from any resource
* context or that is accessible from the same resource context that was used to load the
* Safety Center configuration. The id defaults to {@link Resources#ID_NULL} when a title
* for work is not provided.
*
* The title for work is required if the profile property of the source is set to {@link
* SafetySource#PROFILE_ALL} and either the source is of type static or the source is a
* source of type dynamic that is not hidden and that does not provide search terms. The
* title for work is prohibited for sources of type issue-only and if the profile property
* of the source is not set to {@link SafetySource#PROFILE_ALL}.
*/
@NonNull
public Builder setTitleForWorkResId(@StringRes int titleForWorkResId) {
mTitleForWorkResId = titleForWorkResId;
return this;
}
/**
* Sets the resource id of the title for private profile of this safety source.
*
* The id must refer to a string resource that is either accessible from any resource
* context or that is accessible from the same resource context that was used to load the
* Safety Center configuration. The id defaults to {@link Resources#ID_NULL} when a title
* for private profile is not provided.
*
* The title for private profile is required if the profile property of the source is set
* to {@link SafetySource#PROFILE_ALL} and either the source is of type static or the source
* is a source of type dynamic that is not hidden and that does not provide search terms.
* The title for private profile is prohibited for sources of type issue-only and if the
* profile property of the source is not set to {@link SafetySource#PROFILE_ALL}.
*/
@FlaggedApi(Flags.FLAG_PRIVATE_PROFILE_TITLE_API)
@RequiresApi(VANILLA_ICE_CREAM)
@NonNull
public Builder setTitleForPrivateProfileResId(@StringRes int titleForPrivateProfileResId) {
if (!SdkLevel.isAtLeastV()) {
throw new UnsupportedOperationException(
"setTitleForPrivateProfileResId unsupported for SDKs lower than V");
}
mTitleForPrivateProfileResId = titleForPrivateProfileResId;
return this;
}
/**
* Sets the resource id of the summary of this safety source.
*
* The id must refer to a string resource that is either accessible from any resource
* context or that is accessible from the same resource context that was used to load the
* Safety Center configuration. The id defaults to {@link Resources#ID_NULL} when a summary
* is not provided.
*
* The summary is required for sources of type dynamic that are not hidden. The summary
* is prohibited for sources of type issue-only.
*/
@NonNull
public Builder setSummaryResId(@StringRes int summaryResId) {
mSummaryResId = summaryResId;
return this;
}
/**
* Sets the intent action of this safety source.
*
* An intent created from the intent action should resolve to a public activity. If the
* source is displayed as an entry in the Safety Center page, and if the action is set to
* {@code null} or if it does not resolve to an activity the source will be marked as
* disabled.
*
* The intent action is required for sources of type static and for sources of type
* dynamic that are enabled. The intent action is prohibited for sources of type issue-only.
*/
@NonNull
public Builder setIntentAction(@Nullable String intentAction) {
mIntentAction = intentAction;
return this;
}
/**
* Sets the profile property of this safety source.
*
* The profile property is explicitly required for all source types.
*/
@NonNull
public Builder setProfile(@Profile int profile) {
mProfile = profile;
return this;
}
/**
* Sets the initial display state of this safety source.
*
* The initial display state is prohibited for sources of type static and issue-only.
*/
@NonNull
public Builder setInitialDisplayState(@InitialDisplayState int initialDisplayState) {
mInitialDisplayState = initialDisplayState;
return this;
}
/**
* Sets the maximum severity level of this safety source.
*
* The maximum severity level dictates the maximum severity level values that can be used
* in the source status or the source issues when setting the source data at runtime. A
* source can always send a status severity level of at least {@link
* android.safetycenter.SafetySourceData#SEVERITY_LEVEL_INFORMATION} even if the maximum
* severity level is set to a lower value.
*
* The maximum severity level is prohibited for sources of type static.
*/
@NonNull
public Builder setMaxSeverityLevel(int maxSeverityLevel) {
mMaxSeverityLevel = maxSeverityLevel;
return this;
}
/**
* Sets the resource id of the search terms of this safety source.
*
* The id must refer to a string resource that is either accessible from any resource
* context or that is accessible from the same resource context that was used to load the
* Safety Center configuration. The id defaults to {@link Resources#ID_NULL} when search
* terms are not provided.
*
* The search terms are prohibited for sources of type issue-only.
*/
@NonNull
public Builder setSearchTermsResId(@StringRes int searchTermsResId) {
mSearchTermsResId = searchTermsResId;
return this;
}
/**
* Sets the logging allowed property of this safety source.
*
* The logging allowed property defaults to {@code true}.
*
* The logging allowed property is prohibited for sources of type static.
*/
@NonNull
public Builder setLoggingAllowed(boolean loggingAllowed) {
mLoggingAllowed = loggingAllowed;
return this;
}
/**
* Sets the refresh on page open allowed property of this safety source.
*
* If set to {@code true}, a refresh request will be sent to the source when the Safety
* Center page is opened. The refresh on page open allowed property defaults to {@code
* false}.
*
* The refresh on page open allowed property is prohibited for sources of type static.
*/
@NonNull
public Builder setRefreshOnPageOpenAllowed(boolean refreshOnPageOpenAllowed) {
mRefreshOnPageOpenAllowed = refreshOnPageOpenAllowed;
return this;
}
/**
* Sets the {@link #areNotificationsAllowed()} property of this {@link SafetySource}.
*
* If set to {@code true} Safety Center may post Notifications about issues reported by
* this source.
*
* The default value is {@code false}.
*
* @see #areNotificationsAllowed()
*/
@NonNull
@RequiresApi(UPSIDE_DOWN_CAKE)
public Builder setNotificationsAllowed(boolean notificationsAllowed) {
if (!SdkLevel.isAtLeastU()) {
throw new UnsupportedOperationException(
"Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
mNotificationsAllowed = notificationsAllowed;
return this;
}
/**
* Sets the deduplication group for this source.
*
* Sources which are part of the same deduplication group can coordinate to deduplicate
* issues that they're sending to SafetyCenter by providing the same deduplication
* identifier with those issues.
*
* The deduplication group property is prohibited for sources of type static.
*/
@NonNull
@RequiresApi(UPSIDE_DOWN_CAKE)
public Builder setDeduplicationGroup(@Nullable String deduplicationGroup) {
if (!SdkLevel.isAtLeastU()) {
throw new UnsupportedOperationException(
"Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
mDeduplicationGroup = deduplicationGroup;
return this;
}
/**
* Adds a package certificate hash to the {@link #getPackageCertificateHashes()} property of
* this {@link SafetySource}.
*
* @see #getPackageCertificateHashes()
*/
@NonNull
@RequiresApi(UPSIDE_DOWN_CAKE)
public Builder addPackageCertificateHash(@NonNull String packageCertificateHash) {
if (!SdkLevel.isAtLeastU()) {
throw new UnsupportedOperationException(
"Method not supported on versions lower than UPSIDE_DOWN_CAKE");
}
mPackageCertificateHashes.add(packageCertificateHash);
return this;
}
/**
* Creates the {@link SafetySource} defined by this {@link Builder}.
*
* Throws an {@link IllegalStateException} if any constraint on the safety source is
* violated.
*/
@NonNull
public SafetySource build() {
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 = type == SAFETY_SOURCE_TYPE_STATIC;
boolean isDynamic = type == SAFETY_SOURCE_TYPE_DYNAMIC;
boolean isIssueOnly = type == SAFETY_SOURCE_TYPE_ISSUE_ONLY;
String id = mId;
BuilderUtils.validateId(id, "id", true, false);
String packageName = mPackageName;
BuilderUtils.validateAttribute(
packageName,
"packageName",
isDynamic || isIssueOnly,
isStatic && !SdkLevel.isAtLeastU());
int initialDisplayState =
BuilderUtils.validateIntDef(
mInitialDisplayState,
"initialDisplayState",
false,
isStatic || isIssueOnly,
INITIAL_DISPLAY_STATE_ENABLED,
INITIAL_DISPLAY_STATE_ENABLED,
INITIAL_DISPLAY_STATE_DISABLED,
INITIAL_DISPLAY_STATE_HIDDEN);
boolean isEnabled = initialDisplayState == INITIAL_DISPLAY_STATE_ENABLED;
boolean isHidden = initialDisplayState == INITIAL_DISPLAY_STATE_HIDDEN;
boolean isDynamicNotHidden = isDynamic && !isHidden;
int profile =
BuilderUtils.validateIntDef(
mProfile,
"profile",
true,
false,
PROFILE_NONE,
PROFILE_PRIMARY,
PROFILE_ALL);
boolean hasAllProfiles = profile == PROFILE_ALL;
int searchTermsResId =
BuilderUtils.validateResId(
mSearchTermsResId, "searchTerms", false, isIssueOnly);
boolean isDynamicHiddenWithSearch =
isDynamic && isHidden && searchTermsResId != Resources.ID_NULL;
boolean titleRequired = isDynamicNotHidden || isDynamicHiddenWithSearch || isStatic;
int titleResId =
BuilderUtils.validateResId(mTitleResId, "title", titleRequired, isIssueOnly);
int titleForWorkResId =
BuilderUtils.validateResId(
mTitleForWorkResId,
"titleForWork",
hasAllProfiles && titleRequired,
!hasAllProfiles || isIssueOnly);
int summaryResId =
BuilderUtils.validateResId(
mSummaryResId, "summary", isDynamicNotHidden, isIssueOnly);
String intentAction = mIntentAction;
BuilderUtils.validateAttribute(
intentAction,
"intentAction",
(isDynamic && isEnabled) || isStatic,
isIssueOnly);
int maxSeverityLevel =
BuilderUtils.validateInteger(
mMaxSeverityLevel,
"maxSeverityLevel",
false,
isStatic,
Integer.MAX_VALUE);
boolean loggingAllowed =
BuilderUtils.validateBoolean(
mLoggingAllowed, "loggingAllowed", false, isStatic, true);
boolean refreshOnPageOpenAllowed =
BuilderUtils.validateBoolean(
mRefreshOnPageOpenAllowed,
"refreshOnPageOpenAllowed",
false,
isStatic,
false);
String deduplicationGroup = mDeduplicationGroup;
boolean notificationsAllowed = false;
Set