blob: cee872a3acecd3b2ed40d369ca7901dd6b952685 [file] [log] [blame]
/*
* 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;
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;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.PendingIntent;
import android.os.Parcel;
import android.os.Parcelable;
import android.safetycenter.config.SafetySourcesGroup;
import android.text.TextUtils;
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;
import java.util.List;
import java.util.Objects;
/**
* An issue in the Safety Center.
*
* <p>An issue represents an actionable matter on the device of elevated importance.
*
* <p>It contains localized messages to display to the user, explaining the underlying threat or
* warning and suggested fixes, and contains actions that a user may take from the UI to resolve the
* issue.
*
* <p>Issues are ephemeral and disappear when resolved by user action or dismissal.
*
* @hide
*/
@SystemApi
@RequiresApi(TIRAMISU)
public final class SafetyCenterIssue implements Parcelable {
/** Indicates that this is low-severity, and informational. */
public static final int ISSUE_SEVERITY_LEVEL_OK = 2100;
/** Indicates that this issue describes a safety recommendation. */
public static final int ISSUE_SEVERITY_LEVEL_RECOMMENDATION = 2200;
/** Indicates that this issue describes a critical safety warning. */
public static final int ISSUE_SEVERITY_LEVEL_CRITICAL_WARNING = 2300;
/**
* All possible severity levels for a {@link SafetyCenterIssue}.
*
* @hide
* @see SafetyCenterIssue#getSeverityLevel()
* @see Builder#setSeverityLevel(int)
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(
prefix = "ISSUE_SEVERITY_LEVEL_",
value = {
ISSUE_SEVERITY_LEVEL_OK,
ISSUE_SEVERITY_LEVEL_RECOMMENDATION,
ISSUE_SEVERITY_LEVEL_CRITICAL_WARNING,
})
public @interface IssueSeverityLevel {}
@NonNull
public static final Creator<SafetyCenterIssue> CREATOR =
new Creator<SafetyCenterIssue>() {
@Override
public SafetyCenterIssue createFromParcel(Parcel in) {
String id = in.readString();
CharSequence title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
CharSequence subtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
CharSequence summary = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
SafetyCenterIssue.Builder builder =
new Builder(id, title, summary)
.setSubtitle(subtitle)
.setSeverityLevel(in.readInt())
.setDismissible(in.readBoolean())
.setShouldConfirmDismissal(in.readBoolean())
.setActions(in.createTypedArrayList(Action.CREATOR));
if (SdkLevel.isAtLeastU()) {
builder.setAttributionTitle(
TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in));
builder.setGroupId(in.readString());
}
return builder.build();
}
@Override
public SafetyCenterIssue[] newArray(int size) {
return new SafetyCenterIssue[size];
}
};
@NonNull private final String mId;
@NonNull private final CharSequence mTitle;
@Nullable private final CharSequence mSubtitle;
@NonNull private final CharSequence mSummary;
@IssueSeverityLevel private final int mSeverityLevel;
private final boolean mDismissible;
private final boolean mShouldConfirmDismissal;
@NonNull private final List<Action> mActions;
@Nullable private final CharSequence mAttributionTitle;
@Nullable private final String mGroupId;
private SafetyCenterIssue(
@NonNull String id,
@NonNull CharSequence title,
@Nullable CharSequence subtitle,
@NonNull CharSequence summary,
@IssueSeverityLevel int severityLevel,
boolean isDismissible,
boolean shouldConfirmDismissal,
@NonNull List<Action> actions,
@Nullable CharSequence attributionTitle,
@Nullable String groupId) {
mId = id;
mTitle = title;
mSubtitle = subtitle;
mSummary = summary;
mSeverityLevel = severityLevel;
mDismissible = isDismissible;
mShouldConfirmDismissal = shouldConfirmDismissal;
mActions = actions;
mAttributionTitle = attributionTitle;
mGroupId = groupId;
}
/**
* Returns the encoded string ID which uniquely identifies this issue within the Safety Center
* on the device for the current user across all profiles and accounts.
*/
@NonNull
public String getId() {
return mId;
}
/** Returns the title that describes this issue. */
@NonNull
public CharSequence getTitle() {
return mTitle;
}
/** Returns the subtitle of this issue, or {@code null} if it has none. */
@Nullable
public CharSequence getSubtitle() {
return mSubtitle;
}
/** Returns the summary text that describes this issue. */
@NonNull
public CharSequence getSummary() {
return mSummary;
}
/**
* Returns the attribution title of this issue, or {@code null} if it has none.
*
* <p>This is displayed in the UI and helps to attribute issue cards to a particular source.
*
* @throws UnsupportedOperationException if accessed from a version lower than {@link
* UPSIDE_DOWN_CAKE}
*/
@Nullable
@RequiresApi(UPSIDE_DOWN_CAKE)
public CharSequence getAttributionTitle() {
if (!SdkLevel.isAtLeastU()) {
throw new UnsupportedOperationException(
"Method not supported for versions lower than UPSIDE_DOWN_CAKE");
}
return mAttributionTitle;
}
/** Returns the {@link IssueSeverityLevel} of this issue. */
@IssueSeverityLevel
public int getSeverityLevel() {
return mSeverityLevel;
}
/** Returns {@code true} if this issue can be dismissed. */
public boolean isDismissible() {
return mDismissible;
}
/** Returns {@code true} if this issue should have its dismissal confirmed. */
public boolean shouldConfirmDismissal() {
return mShouldConfirmDismissal;
}
/**
* Returns the ordered list of {@link Action} objects that may be taken to resolve this issue.
*
* <p>An issue may have 0-2 actions. The first action will be considered the "Primary" action of
* the issue.
*/
@NonNull
public List<Action> getActions() {
return mActions;
}
/**
* Returns the ID of the {@link SafetySourcesGroup} that this issue belongs to, or {@code null}
* if it has none.
*
* <p>This ID is used for displaying the issue on its corresponding subpage in the Safety Center
* UI.
*
* @throws UnsupportedOperationException if accessed from a version lower than {@link
* UPSIDE_DOWN_CAKE}
*/
@Nullable
@RequiresApi(UPSIDE_DOWN_CAKE)
public String getGroupId() {
if (!SdkLevel.isAtLeastU()) {
throw new UnsupportedOperationException(
"Method not supported for versions lower than UPSIDE_DOWN_CAKE");
}
return mGroupId;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof SafetyCenterIssue)) return false;
SafetyCenterIssue that = (SafetyCenterIssue) o;
return mSeverityLevel == that.mSeverityLevel
&& mDismissible == that.mDismissible
&& mShouldConfirmDismissal == that.mShouldConfirmDismissal
&& Objects.equals(mId, that.mId)
&& TextUtils.equals(mTitle, that.mTitle)
&& TextUtils.equals(mSubtitle, that.mSubtitle)
&& TextUtils.equals(mSummary, that.mSummary)
&& Objects.equals(mActions, that.mActions)
&& TextUtils.equals(mAttributionTitle, that.mAttributionTitle)
&& Objects.equals(mGroupId, that.mGroupId);
}
@Override
public int hashCode() {
return Objects.hash(
mId,
mTitle,
mSubtitle,
mSummary,
mSeverityLevel,
mDismissible,
mShouldConfirmDismissal,
mActions,
mAttributionTitle,
mGroupId);
}
@Override
public String toString() {
return "SafetyCenterIssue{"
+ "mId="
+ mId
+ ", mTitle="
+ mTitle
+ ", mSubtitle="
+ mSubtitle
+ ", mSummary="
+ mSummary
+ ", mSeverityLevel="
+ mSeverityLevel
+ ", mDismissible="
+ mDismissible
+ ", mConfirmDismissal="
+ mShouldConfirmDismissal
+ ", mActions="
+ mActions
+ ", mAttributionTitle="
+ mAttributionTitle
+ ", mGroupId="
+ mGroupId
+ '}';
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(mId);
TextUtils.writeToParcel(mTitle, dest, flags);
TextUtils.writeToParcel(mSubtitle, dest, flags);
TextUtils.writeToParcel(mSummary, dest, flags);
dest.writeInt(mSeverityLevel);
dest.writeBoolean(mDismissible);
dest.writeBoolean(mShouldConfirmDismissal);
dest.writeTypedList(mActions);
if (SdkLevel.isAtLeastU()) {
TextUtils.writeToParcel(mAttributionTitle, dest, flags);
dest.writeString(mGroupId);
}
}
/** Builder class for {@link SafetyCenterIssue}. */
public static final class Builder {
@NonNull private String mId;
@NonNull private CharSequence mTitle;
@NonNull private CharSequence mSummary;
@Nullable private CharSequence mSubtitle;
@IssueSeverityLevel private int mSeverityLevel = ISSUE_SEVERITY_LEVEL_OK;
private boolean mDismissible = true;
private boolean mShouldConfirmDismissal = true;
private List<Action> mActions = new ArrayList<>();
@Nullable private CharSequence mAttributionTitle;
@Nullable private String mGroupId;
/**
* Creates a {@link Builder} for a {@link SafetyCenterIssue}.
*
* @param id a unique encoded string ID, see {@link #getId()} for details
* @param title a title that describes this issue
* @param summary a summary of this issue
*/
public Builder(
@NonNull String id, @NonNull CharSequence title, @NonNull CharSequence summary) {
mId = requireNonNull(id);
mTitle = requireNonNull(title);
mSummary = requireNonNull(summary);
}
/** Creates a {@link Builder} with the values from the given {@link SafetyCenterIssue}. */
public Builder(@NonNull SafetyCenterIssue issue) {
mId = issue.mId;
mTitle = issue.mTitle;
mSubtitle = issue.mSubtitle;
mSummary = issue.mSummary;
mSeverityLevel = issue.mSeverityLevel;
mDismissible = issue.mDismissible;
mShouldConfirmDismissal = issue.mShouldConfirmDismissal;
mActions = new ArrayList<>(issue.mActions);
mAttributionTitle = issue.mAttributionTitle;
mGroupId = issue.mGroupId;
}
/** Sets the ID for this issue. */
@NonNull
public Builder setId(@NonNull String id) {
mId = requireNonNull(id);
return this;
}
/** Sets the title for this issue. */
@NonNull
public Builder setTitle(@NonNull CharSequence title) {
mTitle = requireNonNull(title);
return this;
}
/** Sets or clears the optional subtitle for this issue. */
@NonNull
public Builder setSubtitle(@Nullable CharSequence subtitle) {
mSubtitle = subtitle;
return this;
}
/** Sets the summary for this issue. */
@NonNull
public Builder setSummary(@NonNull CharSequence summary) {
mSummary = requireNonNull(summary);
return this;
}
/**
* Sets or clears the optional attribution title for this issue.
*
* <p>This is displayed in the UI and helps to attribute issue cards to a particular source.
*
* @throws UnsupportedOperationException if accessed from a version lower than {@link
* UPSIDE_DOWN_CAKE}
*/
@NonNull
@RequiresApi(UPSIDE_DOWN_CAKE)
public Builder setAttributionTitle(@Nullable CharSequence attributionTitle) {
if (!SdkLevel.isAtLeastU()) {
throw new UnsupportedOperationException(
"Method not supported for versions lower than UPSIDE_DOWN_CAKE");
}
mAttributionTitle = attributionTitle;
return this;
}
/**
* Sets {@link IssueSeverityLevel} for this issue. Defaults to {@link
* #ISSUE_SEVERITY_LEVEL_OK}.
*/
@NonNull
public Builder setSeverityLevel(@IssueSeverityLevel int severityLevel) {
mSeverityLevel = validateIssueSeverityLevel(severityLevel);
return this;
}
/** Sets whether this issue can be dismissed. Defaults to {@code true}. */
@NonNull
public Builder setDismissible(boolean dismissible) {
mDismissible = dismissible;
return this;
}
/**
* Sets whether this issue should have its dismissal confirmed. Defaults to {@code true}.
*/
@NonNull
public Builder setShouldConfirmDismissal(boolean confirmDismissal) {
mShouldConfirmDismissal = confirmDismissal;
return this;
}
/**
* Sets the list of potential actions to be taken to resolve this issue. Defaults to an
* empty list.
*/
@NonNull
public Builder setActions(@NonNull List<Action> actions) {
mActions = requireNonNull(actions);
return this;
}
/**
* Sets the ID of {@link SafetySourcesGroup} that this issue belongs to. Defaults to a
* {@code null} value.
*
* <p>This ID is used for displaying the issue on its corresponding subpage in the Safety
* Center UI.
*
* @throws UnsupportedOperationException if accessed from a version lower than {@link
* UPSIDE_DOWN_CAKE}
*/
@NonNull
@RequiresApi(UPSIDE_DOWN_CAKE)
public Builder setGroupId(@Nullable String groupId) {
if (!SdkLevel.isAtLeastU()) {
throw new UnsupportedOperationException(
"Method not supported for versions lower than UPSIDE_DOWN_CAKE");
}
mGroupId = groupId;
return this;
}
/** Creates the {@link SafetyCenterIssue} defined by this {@link Builder}. */
@NonNull
public SafetyCenterIssue build() {
return new SafetyCenterIssue(
mId,
mTitle,
mSubtitle,
mSummary,
mSeverityLevel,
mDismissible,
mShouldConfirmDismissal,
unmodifiableList(new ArrayList<>(mActions)),
mAttributionTitle,
mGroupId);
}
}
/**
* An action that can be taken to resolve a given issue.
*
* <p>When a user initiates an {@link Action}, that action's associated {@link PendingIntent}
* will be executed, and the {@code successMessage} will be displayed if present.
*/
public static final class Action implements Parcelable {
@NonNull
public static final Creator<Action> CREATOR =
new Creator<Action>() {
@Override
public Action createFromParcel(Parcel in) {
String id = in.readString();
CharSequence label = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
PendingIntent pendingIntent = in.readTypedObject(PendingIntent.CREATOR);
Builder builder =
new Builder(id, label, pendingIntent)
.setWillResolve(in.readBoolean())
.setIsInFlight(in.readBoolean())
.setSuccessMessage(
TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(
in));
if (SdkLevel.isAtLeastU()) {
ConfirmationDialogDetails confirmationDialogDetails =
in.readTypedObject(ConfirmationDialogDetails.CREATOR);
builder.setConfirmationDialogDetails(confirmationDialogDetails);
}
return builder.build();
}
@Override
public Action[] newArray(int size) {
return new Action[size];
}
};
@NonNull private final String mId;
@NonNull private final CharSequence mLabel;
@NonNull private final PendingIntent mPendingIntent;
private final boolean mWillResolve;
private final boolean mInFlight;
@Nullable private final CharSequence mSuccessMessage;
@Nullable private final ConfirmationDialogDetails mConfirmationDialogDetails;
private Action(
@NonNull String id,
@NonNull CharSequence label,
@NonNull PendingIntent pendingIntent,
boolean willResolve,
boolean inFlight,
@Nullable CharSequence successMessage,
@Nullable ConfirmationDialogDetails confirmationDialogDetails) {
mId = id;
mLabel = label;
mPendingIntent = pendingIntent;
mWillResolve = willResolve;
mInFlight = inFlight;
mSuccessMessage = successMessage;
mConfirmationDialogDetails = confirmationDialogDetails;
}
/** Returns the ID of this action. */
@NonNull
public String getId() {
return mId;
}
/** Returns a label describing this {@link Action}. */
@NonNull
public CharSequence getLabel() {
return mLabel;
}
/** Returns the {@link PendingIntent} to execute when this {@link Action} is taken. */
@NonNull
public PendingIntent getPendingIntent() {
return mPendingIntent;
}
/**
* Returns whether invoking this action will fix or address the issue sufficiently for it to
* be considered resolved (i.e. the issue will no longer need to be conveyed to the user in
* the UI).
*/
public boolean willResolve() {
return mWillResolve;
}
/**
* Returns whether this action is currently being executed (i.e. the user clicked on a
* button that triggered this action, and now the Safety Center is waiting for the action's
* result).
*/
public boolean isInFlight() {
return mInFlight;
}
/**
* Returns the success message to display after successfully completing this {@link Action}
* or {@code null} if none should be displayed.
*/
@Nullable
public CharSequence getSuccessMessage() {
return mSuccessMessage;
}
/**
* Returns the optional data to be displayed in the confirmation dialog prior to launching
* the {@link PendingIntent} when the action is clicked on.
*/
@Nullable
@RequiresApi(UPSIDE_DOWN_CAKE)
public ConfirmationDialogDetails getConfirmationDialogDetails() {
if (!SdkLevel.isAtLeastU()) {
throw new UnsupportedOperationException();
}
return mConfirmationDialogDetails;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Action)) return false;
Action action = (Action) o;
return Objects.equals(mId, action.mId)
&& TextUtils.equals(mLabel, action.mLabel)
&& Objects.equals(mPendingIntent, action.mPendingIntent)
&& mWillResolve == action.mWillResolve
&& mInFlight == action.mInFlight
&& TextUtils.equals(mSuccessMessage, action.mSuccessMessage)
&& Objects.equals(
mConfirmationDialogDetails, action.mConfirmationDialogDetails);
}
@Override
public int hashCode() {
return Objects.hash(
mId,
mLabel,
mSuccessMessage,
mWillResolve,
mInFlight,
mPendingIntent,
mConfirmationDialogDetails);
}
@Override
public String toString() {
return "Action{"
+ "mId="
+ mId
+ ", mLabel="
+ mLabel
+ ", mPendingIntent="
+ mPendingIntent
+ ", mWillResolve="
+ mWillResolve
+ ", mInFlight="
+ mInFlight
+ ", mSuccessMessage="
+ mSuccessMessage
+ ", mConfirmationDialogDetails="
+ mConfirmationDialogDetails
+ '}';
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(mId);
TextUtils.writeToParcel(mLabel, dest, flags);
dest.writeTypedObject(mPendingIntent, flags);
dest.writeBoolean(mWillResolve);
dest.writeBoolean(mInFlight);
TextUtils.writeToParcel(mSuccessMessage, dest, flags);
if (SdkLevel.isAtLeastU()) {
dest.writeTypedObject(mConfirmationDialogDetails, flags);
}
}
/** Data for an action confirmation dialog to be shown before action is executed. */
@RequiresApi(UPSIDE_DOWN_CAKE)
public static final class ConfirmationDialogDetails implements Parcelable {
@NonNull
public static final Creator<ConfirmationDialogDetails> CREATOR =
new Creator<ConfirmationDialogDetails>() {
@Override
public ConfirmationDialogDetails createFromParcel(Parcel in) {
CharSequence title =
TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
CharSequence text =
TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
CharSequence acceptButtonText =
TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
CharSequence denyButtonText =
TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
return new ConfirmationDialogDetails(
title, text, acceptButtonText, denyButtonText);
}
@Override
public ConfirmationDialogDetails[] newArray(int size) {
return new ConfirmationDialogDetails[size];
}
};
@NonNull private final CharSequence mTitle;
@NonNull private final CharSequence mText;
@NonNull private final CharSequence mAcceptButtonText;
@NonNull private final CharSequence mDenyButtonText;
public ConfirmationDialogDetails(
@NonNull CharSequence title,
@NonNull CharSequence text,
@NonNull CharSequence acceptButtonText,
@NonNull CharSequence denyButtonText) {
mTitle = requireNonNull(title);
mText = requireNonNull(text);
mAcceptButtonText = requireNonNull(acceptButtonText);
mDenyButtonText = requireNonNull(denyButtonText);
}
/** Returns the title of action confirmation dialog. */
@NonNull
public CharSequence getTitle() {
return mTitle;
}
/** Returns the text of action confirmation dialog. */
@NonNull
public CharSequence getText() {
return mText;
}
/** Returns the text of the button to accept action execution. */
@NonNull
public CharSequence getAcceptButtonText() {
return mAcceptButtonText;
}
/** Returns the text of the button to deny action execution. */
@NonNull
public CharSequence getDenyButtonText() {
return mDenyButtonText;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
TextUtils.writeToParcel(mTitle, dest, flags);
TextUtils.writeToParcel(mText, dest, flags);
TextUtils.writeToParcel(mAcceptButtonText, dest, flags);
TextUtils.writeToParcel(mDenyButtonText, dest, flags);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ConfirmationDialogDetails)) return false;
ConfirmationDialogDetails that = (ConfirmationDialogDetails) o;
return TextUtils.equals(mTitle, that.mTitle)
&& TextUtils.equals(mText, that.mText)
&& TextUtils.equals(mAcceptButtonText, that.mAcceptButtonText)
&& TextUtils.equals(mDenyButtonText, that.mDenyButtonText);
}
@Override
public int hashCode() {
return Objects.hash(mTitle, mText, mAcceptButtonText, mDenyButtonText);
}
@Override
public String toString() {
return "ConfirmationDialogDetails{"
+ "mTitle="
+ mTitle
+ ", mText="
+ mText
+ ", mAcceptButtonText="
+ mAcceptButtonText
+ ", mDenyButtonText="
+ mDenyButtonText
+ '}';
}
}
/** Builder class for {@link Action}. */
public static final class Builder {
@NonNull private String mId;
@NonNull private CharSequence mLabel;
@NonNull private PendingIntent mPendingIntent;
private boolean mWillResolve;
private boolean mInFlight;
@Nullable private CharSequence mSuccessMessage;
@Nullable private ConfirmationDialogDetails mConfirmationDialogDetails;
/**
* Creates a new {@link Builder} for an {@link Action}.
*
* @param id a unique ID for this action
* @param label a label describing this action
* @param pendingIntent a {@link PendingIntent} to be sent when this action is taken
*/
public Builder(
@NonNull String id,
@NonNull CharSequence label,
@NonNull PendingIntent pendingIntent) {
mId = requireNonNull(id);
mLabel = requireNonNull(label);
mPendingIntent = requireNonNull(pendingIntent);
}
/** Creates a {@link Builder} with the values from the given {@link Action}. */
@RequiresApi(UPSIDE_DOWN_CAKE)
public Builder(@NonNull Action action) {
if (!SdkLevel.isAtLeastU()) {
throw new UnsupportedOperationException();
}
requireNonNull(action);
mId = action.mId;
mLabel = action.mLabel;
mPendingIntent = action.mPendingIntent;
mWillResolve = action.mWillResolve;
mInFlight = action.mInFlight;
mSuccessMessage = action.mSuccessMessage;
mConfirmationDialogDetails = action.mConfirmationDialogDetails;
}
/** Sets the ID of this {@link Action} */
@NonNull
public Builder setId(@NonNull String id) {
mId = requireNonNull(id);
return this;
}
/** Sets the label of this {@link Action}. */
@NonNull
public Builder setLabel(@NonNull CharSequence label) {
mLabel = requireNonNull(label);
return this;
}
/** Sets the {@link PendingIntent} to be sent when this {@link Action} is taken. */
@NonNull
public Builder setPendingIntent(@NonNull PendingIntent pendingIntent) {
mPendingIntent = requireNonNull(pendingIntent);
return this;
}
/**
* Sets whether this action will resolve the issue when executed. Defaults to {@code
* false}.
*
* @see #willResolve()
*/
@SuppressLint("MissingGetterMatchingBuilder")
@NonNull
public Builder setWillResolve(boolean willResolve) {
mWillResolve = willResolve;
return this;
}
/**
* Sets a boolean that indicates whether this action is currently being executed (i.e.
* the user clicked on a button that triggered this action, and now the Safety Center is
* waiting for the action's result). Defaults to {@code false}.
*
* @see #isInFlight()
*/
@SuppressLint("MissingGetterMatchingBuilder")
@NonNull
public Builder setIsInFlight(boolean inFlight) {
mInFlight = inFlight;
return this;
}
/**
* Sets or clears the optional success message to be displayed when this {@link Action}
* completes.
*/
@NonNull
public Builder setSuccessMessage(@Nullable CharSequence successMessage) {
mSuccessMessage = successMessage;
return this;
}
/**
* Sets the optional data to be displayed in the confirmation dialog prior to launching
* the {@link PendingIntent} when the action is clicked on.
*/
@NonNull
@RequiresApi(UPSIDE_DOWN_CAKE)
public Builder setConfirmationDialogDetails(
@Nullable ConfirmationDialogDetails confirmationDialogDetails) {
if (!SdkLevel.isAtLeastU()) {
throw new UnsupportedOperationException();
}
mConfirmationDialogDetails = confirmationDialogDetails;
return this;
}
/** Creates the {@link Action} defined by this {@link Builder}. */
@NonNull
public Action build() {
return new Action(
mId,
mLabel,
mPendingIntent,
mWillResolve,
mInFlight,
mSuccessMessage,
mConfirmationDialogDetails);
}
}
}
@IssueSeverityLevel
private static int validateIssueSeverityLevel(int value) {
switch (value) {
case ISSUE_SEVERITY_LEVEL_OK:
case ISSUE_SEVERITY_LEVEL_RECOMMENDATION:
case ISSUE_SEVERITY_LEVEL_CRITICAL_WARNING:
return value;
default:
}
throw new IllegalArgumentException(
"Unexpected IssueSeverityLevel for SafetyCenterIssue: " + value);
}
}