• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2014, 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.service.notification;
18 
19 import static com.android.internal.util.Preconditions.checkArgument;
20 
21 import android.annotation.IntDef;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.content.Context;
25 import android.net.Uri;
26 import android.os.Parcel;
27 import android.os.Parcelable;
28 import android.util.proto.ProtoOutputStream;
29 
30 import java.lang.annotation.Retention;
31 import java.lang.annotation.RetentionPolicy;
32 import java.util.Objects;
33 
34 /**
35  * The current condition of an {@link android.app.AutomaticZenRule}, provided by the
36  * app that owns the rule. Used to tell the system to enter Do Not
37  * Disturb mode and request that the system exit Do Not Disturb mode.
38  */
39 public final class Condition implements Parcelable {
40 
41     public static final String SCHEME = "condition";
42 
43     /** @hide */
44     @IntDef(prefix = { "STATE_" }, value = {
45             STATE_FALSE,
46             STATE_TRUE,
47             STATE_UNKNOWN,
48             STATE_ERROR
49     })
50     @Retention(RetentionPolicy.SOURCE)
51     public @interface State {}
52 
53     /**
54      * Indicates that Do Not Disturb should be turned off. Note that all Conditions from all
55      * {@link android.app.AutomaticZenRule} providers must be off for Do Not Disturb to be turned
56      * off on the device.
57      */
58     public static final int STATE_FALSE = 0;
59     /**
60      * Indicates that Do Not Disturb should be turned on.
61      */
62     public static final int STATE_TRUE = 1;
63     public static final int STATE_UNKNOWN = 2;
64     public static final int STATE_ERROR = 3;
65 
66     public static final int FLAG_RELEVANT_NOW = 1 << 0;
67     public static final int FLAG_RELEVANT_ALWAYS = 1 << 1;
68 
69     /**
70      * The URI representing the rule being updated.
71      * See {@link android.app.AutomaticZenRule#getConditionId()}.
72      */
73     public final Uri id;
74 
75     /**
76      * A summary of what the rule encoded in {@link #id} means when it is enabled. User visible
77      * if the state of the condition is {@link #STATE_TRUE}.
78      */
79     public final String summary;
80 
81     public final String line1;
82     public final String line2;
83 
84     /**
85      * The state of this condition. {@link #STATE_TRUE} will enable Do Not Disturb mode.
86      * {@link #STATE_FALSE} will turn Do Not Disturb off for this rule. Note that Do Not Disturb
87      * might still be enabled globally if other conditions are in a {@link #STATE_TRUE} state.
88      */
89     @State
90     public final int state;
91 
92     public final int flags;
93     public final int icon;
94 
95     /** @hide */
96     @IntDef(prefix = { "SOURCE_" }, value = {
97             SOURCE_UNKNOWN,
98             SOURCE_USER_ACTION,
99             SOURCE_SCHEDULE,
100             SOURCE_CONTEXT
101     })
102     @Retention(RetentionPolicy.SOURCE)
103     public @interface Source {}
104 
105     /** The state is changing due to an unknown reason. */
106     public static final int SOURCE_UNKNOWN = 0;
107     /** The state is changing due to an explicit user action. */
108     public static final int SOURCE_USER_ACTION = 1;
109     /** The state is changing due to an automatic schedule (alarm, set time, etc). */
110     public static final int SOURCE_SCHEDULE = 2;
111     /** The state is changing due to a change in context (such as detected driving or sleeping). */
112     public static final int SOURCE_CONTEXT = 3;
113 
114     /** The source of, or reason for, the state change represented by this Condition. **/
115     public final @Source int source; // default = SOURCE_UNKNOWN
116 
117     /**
118      * The maximum string length for any string contained in this condition.
119      * @hide
120      */
121     public static final int MAX_STRING_LENGTH = 1000;
122 
123     /**
124      * An object representing the current state of a {@link android.app.AutomaticZenRule}.
125      * @param id the {@link android.app.AutomaticZenRule#getConditionId()} of the zen rule
126      * @param summary a user visible description of the rule state
127      * @param state whether the mode should be activated or deactivated
128      */
129     // TODO: b/310208502 - Deprecate this in favor of constructor which specifies source.
Condition(Uri id, String summary, int state)130     public Condition(Uri id, String summary, int state) {
131         this(id, summary, "", "", -1, state, SOURCE_UNKNOWN, FLAG_RELEVANT_ALWAYS);
132     }
133 
134     /**
135      * An object representing the current state of a {@link android.app.AutomaticZenRule}.
136      * @param id the {@link android.app.AutomaticZenRule#getConditionId()} of the zen rule
137      * @param summary a user visible description of the rule state
138      * @param state whether the mode should be activated or deactivated
139      * @param source the source of, or reason for, the state change represented by this Condition
140      */
Condition(@ullable Uri id, @Nullable String summary, @State int state, @Source int source)141     public Condition(@Nullable Uri id, @Nullable String summary, @State int state,
142                      @Source int source) {
143         this(id, summary, "", "", -1, state, source, FLAG_RELEVANT_ALWAYS);
144     }
145 
146     // TODO: b/310208502 - Deprecate this in favor of constructor which specifies source.
Condition(Uri id, String summary, String line1, String line2, int icon, int state, int flags)147     public Condition(Uri id, String summary, String line1, String line2, int icon,
148             int state, int flags) {
149         this(id, summary, line1, line2, icon, state, SOURCE_UNKNOWN, flags);
150     }
151 
152     /**
153      * An object representing the current state of a {@link android.app.AutomaticZenRule}.
154      * @param id the {@link android.app.AutomaticZenRule#getConditionId()} of the zen rule
155      * @param summary a user visible description of the rule state
156      * @param line1 a user-visible description of when the rule will end
157      * @param line2 a continuation of the user-visible description of when the rule will end
158      * @param icon an icon representing this condition
159      * @param state whether the mode should be activated or deactivated
160      * @param source the source of, or reason for, the state change represented by this Condition
161      * @param flags flags on this condition
162      */
Condition(@ullable Uri id, @Nullable String summary, @Nullable String line1, @Nullable String line2, int icon, @State int state, @Source int source, int flags)163     public Condition(@Nullable Uri id, @Nullable String summary, @Nullable String line1,
164                      @Nullable String line2, int icon, @State int state, @Source int source,
165                      int flags) {
166         if (id == null) throw new IllegalArgumentException("id is required");
167         if (summary == null) throw new IllegalArgumentException("summary is required");
168         if (!isValidState(state)) throw new IllegalArgumentException("state is invalid: " + state);
169         this.id = getTrimmedUri(id);
170         this.summary = getTrimmedString(summary);
171         this.line1 = getTrimmedString(line1);
172         this.line2 = getTrimmedString(line2);
173         this.icon = icon;
174         this.state = state;
175         this.source = checkValidSource(source);
176         this.flags = flags;
177     }
178 
Condition(Parcel source)179     public Condition(Parcel source) {
180         // This constructor passes all fields directly into the constructor that takes all the
181         // fields as arguments; that constructor will trim each of the input strings to
182         // max length if necessary.
183         this((Uri)source.readParcelable(Condition.class.getClassLoader(), android.net.Uri.class),
184                 source.readString(),
185                 source.readString(),
186                 source.readString(),
187                 source.readInt(),
188                 source.readInt(),
189                 source.readInt(),
190                 source.readInt());
191     }
192 
193     /** @hide */
validate()194     public void validate() {
195         checkValidSource(source);
196     }
197 
isValidState(int state)198     private static boolean isValidState(int state) {
199         return state >= STATE_FALSE && state <= STATE_ERROR;
200     }
201 
checkValidSource(@ource int source)202     private static int checkValidSource(@Source int source) {
203         checkArgument(source >= SOURCE_UNKNOWN && source <= SOURCE_CONTEXT,
204                 "Condition source must be one of SOURCE_UNKNOWN, SOURCE_USER_ACTION, "
205                         + "SOURCE_SCHEDULE, or SOURCE_CONTEXT");
206         return source;
207     }
208 
209     @Override
writeToParcel(Parcel dest, int flags)210     public void writeToParcel(Parcel dest, int flags) {
211         dest.writeParcelable(id, 0);
212         dest.writeString(summary);
213         dest.writeString(line1);
214         dest.writeString(line2);
215         dest.writeInt(icon);
216         dest.writeInt(state);
217         dest.writeInt(this.source);
218         dest.writeInt(this.flags);
219     }
220 
221     @Override
toString()222     public String toString() {
223         return new StringBuilder(Condition.class.getSimpleName()).append('[')
224                 .append("state=").append(stateToString(state))
225                 .append(",id=").append(id)
226                 .append(",summary=").append(summary)
227                 .append(",line1=").append(line1)
228                 .append(",line2=").append(line2)
229                 .append(",icon=").append(icon)
230                 .append(",source=").append(sourceToString(source))
231                 .append(",flags=").append(flags)
232                 .append(']').toString();
233 
234     }
235 
236     /** @hide */
dumpDebug(ProtoOutputStream proto, long fieldId)237     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
238         final long token = proto.start(fieldId);
239 
240         // id is guaranteed not to be null.
241         proto.write(ConditionProto.ID, id.toString());
242         proto.write(ConditionProto.SUMMARY, summary);
243         proto.write(ConditionProto.LINE_1, line1);
244         proto.write(ConditionProto.LINE_2, line2);
245         proto.write(ConditionProto.ICON, icon);
246         proto.write(ConditionProto.STATE, state);
247         // TODO: b/310644464 - Add source to dump.
248         proto.write(ConditionProto.FLAGS, flags);
249 
250         proto.end(token);
251     }
252 
stateToString(int state)253     public static String stateToString(int state) {
254         if (state == STATE_FALSE) return "STATE_FALSE";
255         if (state == STATE_TRUE) return "STATE_TRUE";
256         if (state == STATE_UNKNOWN) return "STATE_UNKNOWN";
257         if (state == STATE_ERROR) return "STATE_ERROR";
258         throw new IllegalArgumentException("state is invalid: " + state);
259     }
260 
261     /**
262      * Provides a human-readable string version of the Source enum.
263      * @hide
264      */
sourceToString(@ource int source)265     public static @NonNull String sourceToString(@Source int source) {
266         if (source == SOURCE_UNKNOWN) return "SOURCE_UNKNOWN";
267         if (source == SOURCE_USER_ACTION) return "SOURCE_USER_ACTION";
268         if (source == SOURCE_SCHEDULE) return "SOURCE_SCHEDULE";
269         if (source == SOURCE_CONTEXT) return "SOURCE_CONTEXT";
270         throw new IllegalArgumentException("source is invalid: " + source);
271     }
272 
relevanceToString(int flags)273     public static String relevanceToString(int flags) {
274         final boolean now = (flags & FLAG_RELEVANT_NOW) != 0;
275         final boolean always = (flags & FLAG_RELEVANT_ALWAYS) != 0;
276         if (!now && !always) return "NONE";
277         if (now && always) return "NOW, ALWAYS";
278         return now ? "NOW" : "ALWAYS";
279     }
280 
281     @Override
equals(@ullable Object o)282     public boolean equals(@Nullable Object o) {
283         if (!(o instanceof Condition)) return false;
284         if (o == this) return true;
285         final Condition other = (Condition) o;
286         return Objects.equals(other.id, id)
287                 && Objects.equals(other.summary, summary)
288                 && Objects.equals(other.line1, line1)
289                 && Objects.equals(other.line2, line2)
290                 && other.icon == icon
291                 && other.state == state
292                 && other.flags == flags
293                 && other.source == source;
294     }
295 
296     @Override
hashCode()297     public int hashCode() {
298         return Objects.hash(id, summary, line1, line2, icon, state, source, flags);
299     }
300 
301     @Override
describeContents()302     public int describeContents() {
303         return 0;
304     }
305 
copy()306     public Condition copy() {
307         final Parcel parcel = Parcel.obtain();
308         try {
309             writeToParcel(parcel, 0);
310             parcel.setDataPosition(0);
311             return new Condition(parcel);
312         } finally {
313             parcel.recycle();
314         }
315     }
316 
newId(Context context)317     public static Uri.Builder newId(Context context) {
318         return new Uri.Builder()
319                 .scheme(Condition.SCHEME)
320                 .authority(context.getPackageName());
321     }
322 
isValidId(Uri id, String pkg)323     public static boolean isValidId(Uri id, String pkg) {
324         return id != null && SCHEME.equals(id.getScheme()) && pkg.equals(id.getAuthority());
325     }
326 
327     public static final @android.annotation.NonNull Parcelable.Creator<Condition> CREATOR
328             = new Parcelable.Creator<Condition>() {
329         @Override
330         public Condition createFromParcel(Parcel source) {
331             return new Condition(source);
332         }
333 
334         @Override
335         public Condition[] newArray(int size) {
336             return new Condition[size];
337         }
338     };
339 
340     /**
341      * Returns a truncated copy of the string if the string is longer than MAX_STRING_LENGTH.
342      */
getTrimmedString(String input)343     private static String getTrimmedString(String input) {
344         if (input != null && input.length() > MAX_STRING_LENGTH) {
345             return input.substring(0, MAX_STRING_LENGTH);
346         }
347         return input;
348     }
349 
350     /**
351      * Returns a truncated copy of the Uri by trimming the string representation to the maximum
352      * string length.
353      */
getTrimmedUri(Uri input)354     private static Uri getTrimmedUri(Uri input) {
355         if (input != null && input.toString().length() > MAX_STRING_LENGTH) {
356             return Uri.parse(getTrimmedString(input.toString()));
357         }
358         return input;
359     }
360 }
361