1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.safetycenter.persistence; 18 19 import static android.os.Build.VERSION_CODES.TIRAMISU; 20 21 import androidx.annotation.Nullable; 22 import androidx.annotation.RequiresApi; 23 24 import java.time.Instant; 25 import java.util.Objects; 26 27 /** 28 * Data class containing all the identifiers and metadata of a safety source issue that should be 29 * persisted. 30 */ 31 @RequiresApi(TIRAMISU) 32 public final class PersistedSafetyCenterIssue { 33 private final String mKey; 34 private final Instant mFirstSeenAt; 35 @Nullable private final Instant mDismissedAt; 36 private final int mDismissCount; 37 @Nullable private final Instant mNotificationDismissedAt; 38 PersistedSafetyCenterIssue( String key, Instant firstSeenAt, @Nullable Instant dismissedAt, int dismissCount, @Nullable Instant notificationDismissedAt)39 private PersistedSafetyCenterIssue( 40 String key, 41 Instant firstSeenAt, 42 @Nullable Instant dismissedAt, 43 int dismissCount, 44 @Nullable Instant notificationDismissedAt) { 45 mKey = key; 46 mFirstSeenAt = firstSeenAt; 47 mDismissedAt = dismissedAt; 48 mDismissCount = dismissCount; 49 mNotificationDismissedAt = notificationDismissedAt; 50 } 51 52 /** The unique key for a safety source issue. */ getKey()53 public String getKey() { 54 return mKey; 55 } 56 57 /** The instant when this issue was first seen. */ getFirstSeenAt()58 public Instant getFirstSeenAt() { 59 return mFirstSeenAt; 60 } 61 62 /** The instant when this issue was dismissed, {@code null} if the issue is not dismissed. */ 63 @Nullable getDismissedAt()64 public Instant getDismissedAt() { 65 return mDismissedAt; 66 } 67 68 /** The number of times this issue was dismissed. */ getDismissCount()69 public int getDismissCount() { 70 return mDismissCount; 71 } 72 73 /** 74 * The instant when the notification for this issue was dismissed, {@code null} if the issue's 75 * notification is not dismissed. 76 */ 77 @Nullable getNotificationDismissedAt()78 public Instant getNotificationDismissedAt() { 79 return mNotificationDismissedAt; 80 } 81 82 @Override equals(Object o)83 public boolean equals(Object o) { 84 if (this == o) return true; 85 if (!(o instanceof PersistedSafetyCenterIssue)) return false; 86 PersistedSafetyCenterIssue that = (PersistedSafetyCenterIssue) o; 87 return Objects.equals(mKey, that.mKey) 88 && Objects.equals(mFirstSeenAt, that.mFirstSeenAt) 89 && Objects.equals(mDismissedAt, that.mDismissedAt) 90 && mDismissCount == that.mDismissCount 91 && Objects.equals(mNotificationDismissedAt, that.mNotificationDismissedAt); 92 } 93 94 @Override hashCode()95 public int hashCode() { 96 return Objects.hash( 97 mKey, mFirstSeenAt, mDismissedAt, mDismissCount, mNotificationDismissedAt); 98 } 99 100 @Override toString()101 public String toString() { 102 return "PersistedSafetyCenterIssue{" 103 + "mKey=" 104 + mKey 105 + ", mFirstSeenAt=" 106 + mFirstSeenAt 107 + ", mDismissedAt=" 108 + mDismissedAt 109 + ", mDismissCount=" 110 + mDismissCount 111 + ", mNotificationDismissedAt=" 112 + mNotificationDismissedAt 113 + '}'; 114 } 115 116 /** Builder class for {@link PersistedSafetyCenterIssue}. */ 117 public static final class Builder { 118 @Nullable private String mKey; 119 @Nullable private Instant mFirstSeenAt; 120 @Nullable private Instant mDismissedAt; 121 private int mDismissCount = 0; 122 @Nullable private Instant mNotificationDismissedAt; 123 124 /** Creates a {@link Builder} for a {@link PersistedSafetyCenterIssue}. */ Builder()125 public Builder() {} 126 127 /** The unique key for a safety source issue. */ setKey(@ullable String key)128 public Builder setKey(@Nullable String key) { 129 mKey = key; 130 return this; 131 } 132 133 /** The instant when this issue was first seen. */ setFirstSeenAt(@ullable Instant firstSeenAt)134 public Builder setFirstSeenAt(@Nullable Instant firstSeenAt) { 135 mFirstSeenAt = firstSeenAt; 136 return this; 137 } 138 139 /** The instant when this issue was dismissed. */ setDismissedAt(@ullable Instant dismissedAt)140 public Builder setDismissedAt(@Nullable Instant dismissedAt) { 141 mDismissedAt = dismissedAt; 142 return this; 143 } 144 145 /** The number of times this issue was dismissed. */ setDismissCount(int dismissCount)146 public Builder setDismissCount(int dismissCount) { 147 if (dismissCount < 0) { 148 throw new IllegalArgumentException("Dismiss count cannot be negative"); 149 } 150 mDismissCount = dismissCount; 151 return this; 152 } 153 154 /** The instant when this issue's notification was dismissed. */ setNotificationDismissedAt(@ullable Instant notificationDismissedAt)155 public Builder setNotificationDismissedAt(@Nullable Instant notificationDismissedAt) { 156 mNotificationDismissedAt = notificationDismissedAt; 157 return this; 158 } 159 160 /** 161 * Creates the {@link PersistedSafetyCenterIssue} defined by this {@link Builder}. 162 * 163 * <p>Throws an {@link IllegalStateException} if any constraint is violated. 164 */ build()165 public PersistedSafetyCenterIssue build() { 166 validateRequiredAttribute(mKey, "key"); 167 validateRequiredAttribute(mFirstSeenAt, "firstSeenAt"); 168 169 if (mDismissedAt != null && mDismissCount == 0) { 170 throw new IllegalStateException( 171 "dismissCount cannot be 0 if dismissedAt is present"); 172 } 173 if (mDismissCount > 0 && mDismissedAt == null) { 174 throw new IllegalStateException( 175 "dismissedAt must be present if dismissCount is greater than 0"); 176 } 177 178 return new PersistedSafetyCenterIssue( 179 mKey, mFirstSeenAt, mDismissedAt, mDismissCount, mNotificationDismissedAt); 180 } 181 } 182 validateRequiredAttribute(@ullable Object attribute, String name)183 private static void validateRequiredAttribute(@Nullable Object attribute, String name) { 184 if (attribute == null) { 185 throw new IllegalStateException("Required attribute " + name + " missing"); 186 } 187 } 188 } 189