blob: c7939be79a2600328baeb5ae6a896ae37b7ac5f5 [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 java.util.Objects.requireNonNull;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.PendingIntent;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import androidx.annotation.RequiresApi;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
* An individual entry in the Safety Center.
*
* <p>A {@link SafetyCenterEntry} conveys the current status of an individual safety feature on the
* device. Entries are present even if they have no associated active issues. In contrast, a {@link
* SafetyCenterIssue} is ephemeral and disappears when the issue is resolved.
*
* <p>Entries link to their corresponding component or an action on it via {@link
* #getPendingIntent()}.
*
* @hide
*/
@SystemApi
@RequiresApi(TIRAMISU)
public final class SafetyCenterEntry implements Parcelable {
/**
* Indicates the severity level of this entry is not currently known. This may be because of an
* error or because some information is missing.
*/
public static final int ENTRY_SEVERITY_LEVEL_UNKNOWN = 3000;
/**
* Indicates this entry does not have a severity level.
*
* <p>This is used when the Safety Center has no opinion on the severity of this entry (e.g. a
* security setting isn't configured, but it's not considered a risk, or for privacy-related
* entries).
*/
public static final int ENTRY_SEVERITY_LEVEL_UNSPECIFIED = 3100;
/** Indicates that there are no problems present with this entry. */
public static final int ENTRY_SEVERITY_LEVEL_OK = 3200;
/** Indicates there are safety recommendations for this entry. */
public static final int ENTRY_SEVERITY_LEVEL_RECOMMENDATION = 3300;
/** Indicates there are critical safety warnings for this entry. */
public static final int ENTRY_SEVERITY_LEVEL_CRITICAL_WARNING = 3400;
/**
* All possible severity levels for a {@link SafetyCenterEntry}.
*
* @hide
* @see SafetyCenterEntry#getSeverityLevel()
* @see Builder#setSeverityLevel(int)
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(
prefix = "ENTRY_SEVERITY_LEVEL_",
value = {
ENTRY_SEVERITY_LEVEL_UNKNOWN,
ENTRY_SEVERITY_LEVEL_UNSPECIFIED,
ENTRY_SEVERITY_LEVEL_OK,
ENTRY_SEVERITY_LEVEL_RECOMMENDATION,
ENTRY_SEVERITY_LEVEL_CRITICAL_WARNING,
})
public @interface EntrySeverityLevel {}
/** Indicates an entry with {@link #ENTRY_SEVERITY_LEVEL_UNSPECIFIED} should not use an icon. */
public static final int SEVERITY_UNSPECIFIED_ICON_TYPE_NO_ICON = 0;
/**
* Indicates an entry with {@link #ENTRY_SEVERITY_LEVEL_UNSPECIFIED} should use the privacy
* icon, for privacy features.
*/
public static final int SEVERITY_UNSPECIFIED_ICON_TYPE_PRIVACY = 1;
/**
* Indicates an entry with {@link #ENTRY_SEVERITY_LEVEL_UNSPECIFIED} should use an icon
* indicating it has no current recommendation.
*/
public static final int SEVERITY_UNSPECIFIED_ICON_TYPE_NO_RECOMMENDATION = 2;
/**
* All possible icon types for a {@link SafetyCenterEntry} to use when its severity level is
* {@link #ENTRY_SEVERITY_LEVEL_UNSPECIFIED}.
*
* <p>It is only relevant when the entry's severity level is {@link
* #ENTRY_SEVERITY_LEVEL_UNSPECIFIED}.
*
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(
prefix = "SEVERITY_UNSPECIFIED_ICON_TYPE_",
value = {
SEVERITY_UNSPECIFIED_ICON_TYPE_NO_ICON,
SEVERITY_UNSPECIFIED_ICON_TYPE_PRIVACY,
SEVERITY_UNSPECIFIED_ICON_TYPE_NO_RECOMMENDATION,
})
public @interface SeverityUnspecifiedIconType {}
@NonNull
public static final Creator<SafetyCenterEntry> CREATOR =
new Creator<SafetyCenterEntry>() {
@Override
public SafetyCenterEntry createFromParcel(Parcel in) {
String id = in.readString();
CharSequence title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
return new Builder(id, title)
.setSummary(TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in))
.setSeverityLevel(in.readInt())
.setSeverityUnspecifiedIconType(in.readInt())
.setEnabled(in.readBoolean())
.setPendingIntent(in.readTypedObject(PendingIntent.CREATOR))
.setIconAction(in.readTypedObject(IconAction.CREATOR))
.build();
}
@Override
public SafetyCenterEntry[] newArray(int size) {
return new SafetyCenterEntry[size];
}
};
@NonNull private final String mId;
@NonNull private final CharSequence mTitle;
@Nullable private final CharSequence mSummary;
@EntrySeverityLevel private final int mSeverityLevel;
@SeverityUnspecifiedIconType private final int mSeverityUnspecifiedIconType;
private final boolean mEnabled;
@Nullable private final PendingIntent mPendingIntent;
@Nullable private final IconAction mIconAction;
private SafetyCenterEntry(
@NonNull String id,
@NonNull CharSequence title,
@Nullable CharSequence summary,
@EntrySeverityLevel int severityLevel,
@SeverityUnspecifiedIconType int severityUnspecifiedIconType,
boolean enabled,
@Nullable PendingIntent pendingIntent,
@Nullable IconAction iconAction) {
mId = id;
mTitle = title;
mSummary = summary;
mSeverityLevel = severityLevel;
mSeverityUnspecifiedIconType = severityUnspecifiedIconType;
mEnabled = enabled;
mPendingIntent = pendingIntent;
mIconAction = iconAction;
}
/**
* Returns the encoded string ID which uniquely identifies this entry 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 entry. */
@NonNull
public CharSequence getTitle() {
return mTitle;
}
/** Returns the summary text that describes this entry if present, or {@code null} otherwise. */
@Nullable
public CharSequence getSummary() {
return mSummary;
}
/** Returns the {@link EntrySeverityLevel} of this entry. */
@EntrySeverityLevel
public int getSeverityLevel() {
return mSeverityLevel;
}
/** Returns the {@link SeverityUnspecifiedIconType} of this entry. */
@SeverityUnspecifiedIconType
public int getSeverityUnspecifiedIconType() {
return mSeverityUnspecifiedIconType;
}
/** Returns whether this entry is enabled. */
public boolean isEnabled() {
return mEnabled;
}
/**
* Returns the optional {@link PendingIntent} to execute when this entry is selected if present,
* or {@code null} otherwise.
*/
@Nullable
public PendingIntent getPendingIntent() {
return mPendingIntent;
}
/**
* Returns the optional {@link IconAction} for this entry if present, or {@code null} otherwise.
*/
@Nullable
public IconAction getIconAction() {
return mIconAction;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof SafetyCenterEntry)) return false;
SafetyCenterEntry that = (SafetyCenterEntry) o;
return mSeverityLevel == that.mSeverityLevel
&& mSeverityUnspecifiedIconType == that.mSeverityUnspecifiedIconType
&& mEnabled == that.mEnabled
&& Objects.equals(mId, that.mId)
&& TextUtils.equals(mTitle, that.mTitle)
&& TextUtils.equals(mSummary, that.mSummary)
&& Objects.equals(mPendingIntent, that.mPendingIntent)
&& Objects.equals(mIconAction, that.mIconAction);
}
@Override
public int hashCode() {
return Objects.hash(
mId,
mTitle,
mSummary,
mSeverityLevel,
mSeverityUnspecifiedIconType,
mEnabled,
mPendingIntent,
mIconAction);
}
@Override
public String toString() {
return "SafetyCenterEntry{"
+ "mId="
+ mId
+ ", mTitle="
+ mTitle
+ ", mSummary="
+ mSummary
+ ", mSeverityLevel="
+ mSeverityLevel
+ ", mSeverityUnspecifiedIconType="
+ mSeverityUnspecifiedIconType
+ ", mEnabled="
+ mEnabled
+ ", mPendingIntent="
+ mPendingIntent
+ ", mIconAction="
+ mIconAction
+ '}';
}
@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(mSummary, dest, flags);
dest.writeInt(mSeverityLevel);
dest.writeInt(mSeverityUnspecifiedIconType);
dest.writeBoolean(mEnabled);
dest.writeTypedObject(mPendingIntent, flags);
dest.writeTypedObject(mIconAction, flags);
}
/** Builder class for {@link SafetyCenterEntry}. */
public static final class Builder {
@NonNull private String mId;
@NonNull private CharSequence mTitle;
@Nullable private CharSequence mSummary;
@EntrySeverityLevel private int mSeverityLevel = ENTRY_SEVERITY_LEVEL_UNKNOWN;
@SeverityUnspecifiedIconType
private int mSeverityUnspecifiedIconType = SEVERITY_UNSPECIFIED_ICON_TYPE_NO_ICON;
private boolean mEnabled = true;
@Nullable private PendingIntent mPendingIntent;
@Nullable private IconAction mIconAction;
/**
* Creates a {@link Builder} for a {@link SafetyCenterEntry}.
*
* @param id a unique encoded string ID, see {@link #getId()} for details
* @param title a title that describes this entry
*/
public Builder(@NonNull String id, @NonNull CharSequence title) {
mId = requireNonNull(id);
mTitle = requireNonNull(title);
}
/** Creates a {@link Builder} with the values from the given {@link SafetyCenterEntry}. */
public Builder(@NonNull SafetyCenterEntry safetyCenterEntry) {
mId = safetyCenterEntry.mId;
mTitle = safetyCenterEntry.mTitle;
mSummary = safetyCenterEntry.mSummary;
mSeverityLevel = safetyCenterEntry.mSeverityLevel;
mSeverityUnspecifiedIconType = safetyCenterEntry.mSeverityUnspecifiedIconType;
mEnabled = safetyCenterEntry.mEnabled;
mPendingIntent = safetyCenterEntry.mPendingIntent;
mIconAction = safetyCenterEntry.mIconAction;
}
/** Sets the ID for this entry. */
@NonNull
public Builder setId(@NonNull String id) {
mId = requireNonNull(id);
return this;
}
/** Sets the title for this entry. */
@NonNull
public Builder setTitle(@NonNull CharSequence title) {
mTitle = requireNonNull(title);
return this;
}
/** Sets the optional summary text for this entry. */
@NonNull
public Builder setSummary(@Nullable CharSequence summary) {
mSummary = summary;
return this;
}
/**
* Sets the {@link EntrySeverityLevel} for this entry. Defaults to {@link
* #ENTRY_SEVERITY_LEVEL_UNKNOWN}.
*/
@NonNull
public Builder setSeverityLevel(@EntrySeverityLevel int severityLevel) {
mSeverityLevel = validateEntrySeverityLevel(severityLevel);
return this;
}
/**
* Sets the {@link SeverityUnspecifiedIconType} for this entry. Defaults to {@link
* #SEVERITY_UNSPECIFIED_ICON_TYPE_NO_ICON}.
*/
@NonNull
public Builder setSeverityUnspecifiedIconType(
@SeverityUnspecifiedIconType int severityUnspecifiedIconType) {
mSeverityUnspecifiedIconType =
validateSeverityUnspecifiedIconType(severityUnspecifiedIconType);
return this;
}
/** Sets whether this entry is enabled. Defaults to {@code true}. */
@NonNull
public Builder setEnabled(boolean enabled) {
mEnabled = enabled;
return this;
}
/** Sets the optional {@link PendingIntent} to execute when this entry is selected. */
@NonNull
public Builder setPendingIntent(@Nullable PendingIntent pendingIntent) {
mPendingIntent = pendingIntent;
return this;
}
/** Sets the optional {@link IconAction} for this entry. */
@NonNull
public Builder setIconAction(@Nullable IconAction iconAction) {
mIconAction = iconAction;
return this;
}
/** Sets the optional {@link IconAction} for this entry. */
@NonNull
public Builder setIconAction(
@IconAction.IconActionType int type, @NonNull PendingIntent pendingIntent) {
mIconAction = new IconAction(type, pendingIntent);
return this;
}
/** Creates the {@link SafetyCenterEntry} defined by this {@link Builder}. */
@NonNull
public SafetyCenterEntry build() {
return new SafetyCenterEntry(
mId,
mTitle,
mSummary,
mSeverityLevel,
mSeverityUnspecifiedIconType,
mEnabled,
mPendingIntent,
mIconAction);
}
}
/** An optional additional action with an icon for a {@link SafetyCenterEntry}. */
public static final class IconAction implements Parcelable {
/** A gear-type icon action, e.g. that links to a settings page for a specific entry. */
public static final int ICON_ACTION_TYPE_GEAR = 30100;
/**
* An info-type icon action, e.g. that displays some additional detailed info about a
* specific entry.
*/
public static final int ICON_ACTION_TYPE_INFO = 30200;
/**
* All possible icon action types.
*
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(
prefix = "ICON_ACTION_TYPE_",
value = {
ICON_ACTION_TYPE_GEAR,
ICON_ACTION_TYPE_INFO,
})
public @interface IconActionType {}
@NonNull
public static final Creator<IconAction> CREATOR =
new Creator<IconAction>() {
@Override
public IconAction createFromParcel(Parcel in) {
int type = in.readInt();
PendingIntent pendingIntent = in.readTypedObject(PendingIntent.CREATOR);
return new IconAction(type, pendingIntent);
}
@Override
public IconAction[] newArray(int size) {
return new IconAction[size];
}
};
@IconActionType private final int mType;
@NonNull private final PendingIntent mPendingIntent;
/** Creates an icon action for a {@link SafetyCenterEntry}. */
public IconAction(@IconActionType int type, @NonNull PendingIntent pendingIntent) {
mType = validateIconActionType(type);
mPendingIntent = requireNonNull(pendingIntent);
}
/** Returns the {@link IconActionType} of this icon action. */
@IconActionType
public int getType() {
return mType;
}
/** Returns the {@link PendingIntent} to execute when this icon action is selected. */
@NonNull
public PendingIntent getPendingIntent() {
return mPendingIntent;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof IconAction)) return false;
IconAction that = (IconAction) o;
return mType == that.mType && Objects.equals(mPendingIntent, that.mPendingIntent);
}
@Override
public int hashCode() {
return Objects.hash(mType, mPendingIntent);
}
@Override
public String toString() {
return "IconAction{" + "mType=" + mType + ", mPendingIntent=" + mPendingIntent + '}';
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mType);
dest.writeTypedObject(mPendingIntent, flags);
}
@IconActionType
private static int validateIconActionType(int value) {
switch (value) {
case ICON_ACTION_TYPE_GEAR:
case ICON_ACTION_TYPE_INFO:
return value;
default:
}
throw new IllegalArgumentException(
"Unexpected IconActionType for IconAction: " + value);
}
}
@EntrySeverityLevel
private static int validateEntrySeverityLevel(int value) {
switch (value) {
case ENTRY_SEVERITY_LEVEL_UNKNOWN:
case ENTRY_SEVERITY_LEVEL_UNSPECIFIED:
case ENTRY_SEVERITY_LEVEL_OK:
case ENTRY_SEVERITY_LEVEL_RECOMMENDATION:
case ENTRY_SEVERITY_LEVEL_CRITICAL_WARNING:
return value;
default:
}
throw new IllegalArgumentException(
"Unexpected EntrySeverityLevel for SafetyCenterEntry: " + value);
}
@SeverityUnspecifiedIconType
private static int validateSeverityUnspecifiedIconType(int value) {
switch (value) {
case SEVERITY_UNSPECIFIED_ICON_TYPE_NO_ICON:
case SEVERITY_UNSPECIFIED_ICON_TYPE_PRIVACY:
case SEVERITY_UNSPECIFIED_ICON_TYPE_NO_RECOMMENDATION:
return value;
default:
}
throw new IllegalArgumentException(
"Unexpected SeverityUnspecifiedIconType for SafetyCenterEntry: " + value);
}
}