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 android.annotation.IntDef;
20 import android.annotation.Nullable;
21 import android.content.Context;
22 import android.net.Uri;
23 import android.os.Parcel;
24 import android.os.Parcelable;
25 import android.util.proto.ProtoOutputStream;
26 
27 import java.lang.annotation.Retention;
28 import java.lang.annotation.RetentionPolicy;
29 import java.util.Objects;
30 
31 /**
32  * The current condition of an {@link android.app.AutomaticZenRule}, provided by the
33  * app that owns the rule. Used to tell the system to enter Do Not
34  * Disturb mode and request that the system exit Do Not Disturb mode.
35  */
36 public final class Condition implements Parcelable {
37 
38     public static final String SCHEME = "condition";
39 
40     /** @hide */
41     @IntDef(prefix = { "STATE_" }, value = {
42             STATE_FALSE,
43             STATE_TRUE,
44             STATE_UNKNOWN,
45             STATE_ERROR
46     })
47     @Retention(RetentionPolicy.SOURCE)
48     public @interface State {}
49 
50     /**
51      * Indicates that Do Not Disturb should be turned off. Note that all Conditions from all
52      * {@link android.app.AutomaticZenRule} providers must be off for Do Not Disturb to be turned
53      * off on the device.
54      */
55     public static final int STATE_FALSE = 0;
56     /**
57      * Indicates that Do Not Disturb should be turned on.
58      */
59     public static final int STATE_TRUE = 1;
60 
61     public static final int STATE_UNKNOWN = 2;
62     public static final int STATE_ERROR = 3;
63 
64     public static final int FLAG_RELEVANT_NOW = 1 << 0;
65     public static final int FLAG_RELEVANT_ALWAYS = 1 << 1;
66 
67     /**
68      * The URI representing the rule being updated.
69      * See {@link android.app.AutomaticZenRule#getConditionId()}.
70      */
71     public final Uri id;
72 
73     /**
74      * A summary of what the rule encoded in {@link #id} means when it is enabled. User visible
75      * if the state of the condition is {@link #STATE_TRUE}.
76      */
77     public final String summary;
78 
79     public final String line1;
80     public final String line2;
81 
82     /**
83      * The state of this condition. {@link #STATE_TRUE} will enable Do Not Disturb mode.
84      * {@link #STATE_FALSE} will turn Do Not Disturb off for this rule. Note that Do Not Disturb
85      * might still be enabled globally if other conditions are in a {@link #STATE_TRUE} state.
86      */
87     @State
88     public final int state;
89 
90     public final int flags;
91     public final int icon;
92 
93     /**
94      * The maximum string length for any string contained in this condition.
95      * @hide
96      */
97     public static final int MAX_STRING_LENGTH = 1000;
98 
99     /**
100      * An object representing the current state of a {@link android.app.AutomaticZenRule}.
101      * @param id the {@link android.app.AutomaticZenRule#getConditionId()} of the zen rule
102      * @param summary a user visible description of the rule state.
103      */
Condition(Uri id, String summary, int state)104     public Condition(Uri id, String summary, int state) {
105         this(id, summary, "", "", -1, state, FLAG_RELEVANT_ALWAYS);
106     }
107 
Condition(Uri id, String summary, String line1, String line2, int icon, int state, int flags)108     public Condition(Uri id, String summary, String line1, String line2, int icon,
109             int state, int flags) {
110         if (id == null) throw new IllegalArgumentException("id is required");
111         if (summary == null) throw new IllegalArgumentException("summary is required");
112         if (!isValidState(state)) throw new IllegalArgumentException("state is invalid: " + state);
113         this.id = getTrimmedUri(id);
114         this.summary = getTrimmedString(summary);
115         this.line1 = getTrimmedString(line1);
116         this.line2 = getTrimmedString(line2);
117         this.icon = icon;
118         this.state = state;
119         this.flags = flags;
120     }
121 
Condition(Parcel source)122     public Condition(Parcel source) {
123         // This constructor passes all fields directly into the constructor that takes all the
124         // fields as arguments; that constructor will trim each of the input strings to
125         // max length if necessary.
126         this((Uri)source.readParcelable(Condition.class.getClassLoader(), android.net.Uri.class),
127                 source.readString(),
128                 source.readString(),
129                 source.readString(),
130                 source.readInt(),
131                 source.readInt(),
132                 source.readInt());
133     }
134 
isValidState(int state)135     private static boolean isValidState(int state) {
136         return state >= STATE_FALSE && state <= STATE_ERROR;
137     }
138 
139     @Override
writeToParcel(Parcel dest, int flags)140     public void writeToParcel(Parcel dest, int flags) {
141         dest.writeParcelable(id, 0);
142         dest.writeString(summary);
143         dest.writeString(line1);
144         dest.writeString(line2);
145         dest.writeInt(icon);
146         dest.writeInt(state);
147         dest.writeInt(this.flags);
148     }
149 
150     @Override
toString()151     public String toString() {
152         return new StringBuilder(Condition.class.getSimpleName()).append('[')
153                 .append("state=").append(stateToString(state))
154                 .append(",id=").append(id)
155                 .append(",summary=").append(summary)
156                 .append(",line1=").append(line1)
157                 .append(",line2=").append(line2)
158                 .append(",icon=").append(icon)
159                 .append(",flags=").append(flags)
160                 .append(']').toString();
161     }
162 
163     /** @hide */
dumpDebug(ProtoOutputStream proto, long fieldId)164     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
165         final long token = proto.start(fieldId);
166 
167         // id is guaranteed not to be null.
168         proto.write(ConditionProto.ID, id.toString());
169         proto.write(ConditionProto.SUMMARY, summary);
170         proto.write(ConditionProto.LINE_1, line1);
171         proto.write(ConditionProto.LINE_2, line2);
172         proto.write(ConditionProto.ICON, icon);
173         proto.write(ConditionProto.STATE, state);
174         proto.write(ConditionProto.FLAGS, flags);
175 
176         proto.end(token);
177     }
178 
stateToString(int state)179     public static String stateToString(int state) {
180         if (state == STATE_FALSE) return "STATE_FALSE";
181         if (state == STATE_TRUE) return "STATE_TRUE";
182         if (state == STATE_UNKNOWN) return "STATE_UNKNOWN";
183         if (state == STATE_ERROR) return "STATE_ERROR";
184         throw new IllegalArgumentException("state is invalid: " + state);
185     }
186 
relevanceToString(int flags)187     public static String relevanceToString(int flags) {
188         final boolean now = (flags & FLAG_RELEVANT_NOW) != 0;
189         final boolean always = (flags & FLAG_RELEVANT_ALWAYS) != 0;
190         if (!now && !always) return "NONE";
191         if (now && always) return "NOW, ALWAYS";
192         return now ? "NOW" : "ALWAYS";
193     }
194 
195     @Override
equals(@ullable Object o)196     public boolean equals(@Nullable Object o) {
197         if (!(o instanceof Condition)) return false;
198         if (o == this) return true;
199         final Condition other = (Condition) o;
200         return Objects.equals(other.id, id)
201                 && Objects.equals(other.summary, summary)
202                 && Objects.equals(other.line1, line1)
203                 && Objects.equals(other.line2, line2)
204                 && other.icon == icon
205                 && other.state == state
206                 && other.flags == flags;
207     }
208 
209     @Override
hashCode()210     public int hashCode() {
211         return Objects.hash(id, summary, line1, line2, icon, state, flags);
212     }
213 
214     @Override
describeContents()215     public int describeContents() {
216         return 0;
217     }
218 
copy()219     public Condition copy() {
220         final Parcel parcel = Parcel.obtain();
221         try {
222             writeToParcel(parcel, 0);
223             parcel.setDataPosition(0);
224             return new Condition(parcel);
225         } finally {
226             parcel.recycle();
227         }
228     }
229 
newId(Context context)230     public static Uri.Builder newId(Context context) {
231         return new Uri.Builder()
232                 .scheme(Condition.SCHEME)
233                 .authority(context.getPackageName());
234     }
235 
isValidId(Uri id, String pkg)236     public static boolean isValidId(Uri id, String pkg) {
237         return id != null && SCHEME.equals(id.getScheme()) && pkg.equals(id.getAuthority());
238     }
239 
240     public static final @android.annotation.NonNull Parcelable.Creator<Condition> CREATOR
241             = new Parcelable.Creator<Condition>() {
242         @Override
243         public Condition createFromParcel(Parcel source) {
244             return new Condition(source);
245         }
246 
247         @Override
248         public Condition[] newArray(int size) {
249             return new Condition[size];
250         }
251     };
252 
253     /**
254      * Returns a truncated copy of the string if the string is longer than MAX_STRING_LENGTH.
255      */
getTrimmedString(String input)256     private static String getTrimmedString(String input) {
257         if (input != null && input.length() > MAX_STRING_LENGTH) {
258             return input.substring(0, MAX_STRING_LENGTH);
259         }
260         return input;
261     }
262 
263     /**
264      * Returns a truncated copy of the Uri by trimming the string representation to the maximum
265      * string length.
266      */
getTrimmedUri(Uri input)267     private static Uri getTrimmedUri(Uri input) {
268         if (input != null && input.toString().length() > MAX_STRING_LENGTH) {
269             return Uri.parse(getTrimmedString(input.toString()));
270         }
271         return input;
272     }
273 }
274