• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 android.safetycenter;
18 
19 import static android.os.Build.VERSION_CODES.TIRAMISU;
20 
21 import static com.android.internal.util.Preconditions.checkArgument;
22 
23 import static java.util.Objects.requireNonNull;
24 
25 import android.annotation.IntDef;
26 import android.annotation.NonNull;
27 import android.annotation.Nullable;
28 import android.annotation.SystemApi;
29 import android.app.PendingIntent;
30 import android.os.Parcel;
31 import android.os.Parcelable;
32 import android.text.TextUtils;
33 
34 import androidx.annotation.RequiresApi;
35 
36 import java.lang.annotation.Retention;
37 import java.lang.annotation.RetentionPolicy;
38 import java.util.Objects;
39 
40 /**
41  * Data for a safety source status in the Safety Center page, which conveys the overall state of the
42  * safety source and allows a user to navigate to the source.
43  *
44  * @hide
45  */
46 @SystemApi
47 @RequiresApi(TIRAMISU)
48 public final class SafetySourceStatus implements Parcelable {
49 
50     @NonNull
51     public static final Creator<SafetySourceStatus> CREATOR =
52             new Creator<SafetySourceStatus>() {
53                 @Override
54                 public SafetySourceStatus createFromParcel(Parcel in) {
55                     CharSequence title = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
56                     CharSequence summary = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
57                     int severityLevel = in.readInt();
58                     return new Builder(title, summary, severityLevel)
59                             .setPendingIntent(in.readTypedObject(PendingIntent.CREATOR))
60                             .setIconAction(in.readTypedObject(IconAction.CREATOR))
61                             .setEnabled(in.readBoolean())
62                             .build();
63                 }
64 
65                 @Override
66                 public SafetySourceStatus[] newArray(int size) {
67                     return new SafetySourceStatus[size];
68                 }
69             };
70 
71     @NonNull private final CharSequence mTitle;
72     @NonNull private final CharSequence mSummary;
73     @SafetySourceData.SeverityLevel private final int mSeverityLevel;
74     @Nullable private final PendingIntent mPendingIntent;
75     @Nullable private final IconAction mIconAction;
76     private final boolean mEnabled;
77 
SafetySourceStatus( @onNull CharSequence title, @NonNull CharSequence summary, @SafetySourceData.SeverityLevel int severityLevel, @Nullable PendingIntent pendingIntent, @Nullable IconAction iconAction, boolean enabled)78     private SafetySourceStatus(
79             @NonNull CharSequence title,
80             @NonNull CharSequence summary,
81             @SafetySourceData.SeverityLevel int severityLevel,
82             @Nullable PendingIntent pendingIntent,
83             @Nullable IconAction iconAction,
84             boolean enabled) {
85         this.mTitle = title;
86         this.mSummary = summary;
87         this.mSeverityLevel = severityLevel;
88         this.mPendingIntent = pendingIntent;
89         this.mIconAction = iconAction;
90         this.mEnabled = enabled;
91     }
92 
93     /** Returns the localized title of the safety source status to be displayed in the UI. */
94     @NonNull
getTitle()95     public CharSequence getTitle() {
96         return mTitle;
97     }
98 
99     /** Returns the localized summary of the safety source status to be displayed in the UI. */
100     @NonNull
getSummary()101     public CharSequence getSummary() {
102         return mSummary;
103     }
104 
105     /** Returns the {@link SafetySourceData.SeverityLevel} of the status. */
106     @SafetySourceData.SeverityLevel
getSeverityLevel()107     public int getSeverityLevel() {
108         return mSeverityLevel;
109     }
110 
111     /**
112      * Returns an optional {@link PendingIntent} that will start an activity when the safety source
113      * status UI is clicked on.
114      *
115      * <p>The action contained in the {@link PendingIntent} must start an activity.
116      *
117      * <p>If {@code null} the intent action defined in the Safety Center configuration will be
118      * invoked when the safety source status UI is clicked on. If the intent action is undefined or
119      * disabled the source is considered as disabled.
120      */
121     @Nullable
getPendingIntent()122     public PendingIntent getPendingIntent() {
123         return mPendingIntent;
124     }
125 
126     /**
127      * Returns an optional {@link IconAction} to be displayed in the safety source status UI.
128      *
129      * <p>The icon action will be a clickable icon which performs an action as indicated by the
130      * icon.
131      */
132     @Nullable
getIconAction()133     public IconAction getIconAction() {
134         return mIconAction;
135     }
136 
137     /**
138      * Returns whether the safety source status is enabled.
139      *
140      * <p>A safety source status should be disabled if it is currently unavailable on the device
141      *
142      * <p>If disabled, the status will show as grayed out in the UI, and interactions with it may be
143      * limited.
144      */
isEnabled()145     public boolean isEnabled() {
146         return mEnabled;
147     }
148 
149     @Override
describeContents()150     public int describeContents() {
151         return 0;
152     }
153 
154     @Override
writeToParcel(@onNull Parcel dest, int flags)155     public void writeToParcel(@NonNull Parcel dest, int flags) {
156         TextUtils.writeToParcel(mTitle, dest, flags);
157         TextUtils.writeToParcel(mSummary, dest, flags);
158         dest.writeInt(mSeverityLevel);
159         dest.writeTypedObject(mPendingIntent, flags);
160         dest.writeTypedObject(mIconAction, flags);
161         dest.writeBoolean(mEnabled);
162     }
163 
164     @Override
equals(Object o)165     public boolean equals(Object o) {
166         if (this == o) return true;
167         if (!(o instanceof SafetySourceStatus)) return false;
168         SafetySourceStatus that = (SafetySourceStatus) o;
169         return mSeverityLevel == that.mSeverityLevel
170                 && mEnabled == that.mEnabled
171                 && TextUtils.equals(mTitle, that.mTitle)
172                 && TextUtils.equals(mSummary, that.mSummary)
173                 && Objects.equals(mPendingIntent, that.mPendingIntent)
174                 && Objects.equals(mIconAction, that.mIconAction);
175     }
176 
177     @Override
hashCode()178     public int hashCode() {
179         return Objects.hash(
180                 mTitle, mSummary, mSeverityLevel, mPendingIntent, mIconAction, mEnabled);
181     }
182 
183     @Override
toString()184     public String toString() {
185         return "SafetySourceStatus{"
186                 + "mTitle="
187                 + mTitle
188                 + ", mSummary="
189                 + mSummary
190                 + ", mSeverityLevel="
191                 + mSeverityLevel
192                 + ", mPendingIntent="
193                 + mPendingIntent
194                 + ", mIconAction="
195                 + mIconAction
196                 + ", mEnabled="
197                 + mEnabled
198                 + '}';
199     }
200 
201     /**
202      * Data for an action supported from a safety source status {@link SafetySourceStatus} in the
203      * Safety Center page.
204      *
205      * <p>The purpose of the action is to add a surface to allow the user to perform an action
206      * relating to the safety source status.
207      *
208      * <p>The action will be shown as a clickable icon chosen from a predefined set of icons (see
209      * {@link IconType}). The icon should indicate to the user what action will be performed on
210      * clicking on it.
211      *
212      * @hide
213      */
214     @SystemApi
215     public static final class IconAction implements Parcelable {
216 
217         @NonNull
218         public static final Creator<IconAction> CREATOR =
219                 new Creator<IconAction>() {
220                     @Override
221                     public IconAction createFromParcel(Parcel in) {
222                         int iconType = in.readInt();
223                         PendingIntent pendingIntent = in.readTypedObject(PendingIntent.CREATOR);
224                         return new IconAction(iconType, pendingIntent);
225                     }
226 
227                     @Override
228                     public IconAction[] newArray(int size) {
229                         return new IconAction[size];
230                     }
231                 };
232 
233         /** Indicates a gear (cog) icon. */
234         public static final int ICON_TYPE_GEAR = 100;
235 
236         /** Indicates an information icon. */
237         public static final int ICON_TYPE_INFO = 200;
238 
239         /**
240          * All possible icons which can be displayed in an {@link IconAction}.
241          *
242          * @hide
243          */
244         @IntDef(
245                 prefix = {"ICON_TYPE_"},
246                 value = {
247                     ICON_TYPE_GEAR,
248                     ICON_TYPE_INFO,
249                 })
250         @Retention(RetentionPolicy.SOURCE)
251         public @interface IconType {}
252 
253         @IconType private final int mIconType;
254         @NonNull private final PendingIntent mPendingIntent;
255 
IconAction(@conType int iconType, @NonNull PendingIntent pendingIntent)256         public IconAction(@IconType int iconType, @NonNull PendingIntent pendingIntent) {
257             this.mIconType = validateIconType(iconType);
258             this.mPendingIntent = requireNonNull(pendingIntent);
259         }
260 
261         /**
262          * Returns the type of icon to be displayed in the UI.
263          *
264          * <p>The icon type should indicate what action will be performed if when invoked.
265          */
266         @IconType
getIconType()267         public int getIconType() {
268             return mIconType;
269         }
270 
271         /**
272          * Returns a {@link PendingIntent} that will start an activity when the icon action is
273          * clicked on.
274          */
275         @NonNull
getPendingIntent()276         public PendingIntent getPendingIntent() {
277             return mPendingIntent;
278         }
279 
280         @Override
describeContents()281         public int describeContents() {
282             return 0;
283         }
284 
285         @Override
writeToParcel(@onNull Parcel dest, int flags)286         public void writeToParcel(@NonNull Parcel dest, int flags) {
287             dest.writeInt(mIconType);
288             dest.writeTypedObject(mPendingIntent, flags);
289         }
290 
291         @Override
equals(Object o)292         public boolean equals(Object o) {
293             if (this == o) return true;
294             if (!(o instanceof IconAction)) return false;
295             IconAction that = (IconAction) o;
296             return mIconType == that.mIconType && mPendingIntent.equals(that.mPendingIntent);
297         }
298 
299         @Override
hashCode()300         public int hashCode() {
301             return Objects.hash(mIconType, mPendingIntent);
302         }
303 
304         @Override
toString()305         public String toString() {
306             return "IconAction{"
307                     + "mIconType="
308                     + mIconType
309                     + ", mPendingIntent="
310                     + mPendingIntent
311                     + '}';
312         }
313 
314         @IconType
validateIconType(int value)315         private static int validateIconType(int value) {
316             switch (value) {
317                 case ICON_TYPE_GEAR:
318                 case ICON_TYPE_INFO:
319                     return value;
320                 default:
321             }
322             throw new IllegalArgumentException(
323                     String.format("Unexpected IconType for IconAction: %s", value));
324         }
325     }
326 
327     /** Builder class for {@link SafetySourceStatus}. */
328     public static final class Builder {
329 
330         @NonNull private final CharSequence mTitle;
331         @NonNull private final CharSequence mSummary;
332         @SafetySourceData.SeverityLevel private final int mSeverityLevel;
333 
334         @Nullable private PendingIntent mPendingIntent;
335         @Nullable private IconAction mIconAction;
336         private boolean mEnabled = true;
337 
338         /** Creates a {@link Builder} for a {@link SafetySourceStatus}. */
Builder( @onNull CharSequence title, @NonNull CharSequence summary, @SafetySourceData.SeverityLevel int severityLevel)339         public Builder(
340                 @NonNull CharSequence title,
341                 @NonNull CharSequence summary,
342                 @SafetySourceData.SeverityLevel int severityLevel) {
343             this.mTitle = requireNonNull(title);
344             this.mSummary = requireNonNull(summary);
345             this.mSeverityLevel = validateSeverityLevel(severityLevel);
346         }
347 
348         /**
349          * Sets an optional {@link PendingIntent} for the safety source status.
350          *
351          * <p>The action contained in the {@link PendingIntent} must start an activity.
352          *
353          * @see #getPendingIntent()
354          */
355         @NonNull
setPendingIntent(@ullable PendingIntent pendingIntent)356         public Builder setPendingIntent(@Nullable PendingIntent pendingIntent) {
357             checkArgument(
358                     pendingIntent == null || pendingIntent.isActivity(),
359                     "Safety source status pending intent must start an activity");
360             this.mPendingIntent = pendingIntent;
361             return this;
362         }
363 
364         /**
365          * Sets an optional {@link IconAction} for the safety source status.
366          *
367          * @see #getIconAction()
368          */
369         @NonNull
setIconAction(@ullable IconAction iconAction)370         public Builder setIconAction(@Nullable IconAction iconAction) {
371             this.mIconAction = iconAction;
372             return this;
373         }
374 
375         /**
376          * Sets whether the safety source status is enabled.
377          *
378          * <p>By default, the safety source status will be enabled. If disabled, the status severity
379          * level must be set to {@link SafetySourceData#SEVERITY_LEVEL_UNSPECIFIED}.
380          *
381          * @see #isEnabled()
382          */
383         @NonNull
setEnabled(boolean enabled)384         public Builder setEnabled(boolean enabled) {
385             checkArgument(
386                     enabled || mSeverityLevel == SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED,
387                     "Safety source status must have a severity level of "
388                             + "SEVERITY_LEVEL_UNSPECIFIED when disabled");
389             this.mEnabled = enabled;
390             return this;
391         }
392 
393         /** Creates the {@link SafetySourceStatus} defined by this {@link Builder}. */
394         @NonNull
build()395         public SafetySourceStatus build() {
396             return new SafetySourceStatus(
397                     mTitle, mSummary, mSeverityLevel, mPendingIntent, mIconAction, mEnabled);
398         }
399     }
400 
401     @SafetySourceData.SeverityLevel
validateSeverityLevel(int value)402     private static int validateSeverityLevel(int value) {
403         switch (value) {
404             case SafetySourceData.SEVERITY_LEVEL_UNSPECIFIED:
405             case SafetySourceData.SEVERITY_LEVEL_INFORMATION:
406             case SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION:
407             case SafetySourceData.SEVERITY_LEVEL_CRITICAL_WARNING:
408                 return value;
409             default:
410         }
411         throw new IllegalArgumentException(
412                 String.format("Unexpected SeverityLevel for SafetySourceStatus: %s", value));
413     }
414 }
415