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