blob: 6911b0161704745f00ec2cd6e159d75f6605990d [file] [log] [blame]
John Spurlock056c5192014-04-20 21:52:01 -04001/**
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
17package android.service.notification;
18
John Spurlock1b8b22b2015-05-20 09:47:13 -040019import android.app.ActivityManager;
John Spurlock1fc476d2015-04-14 16:05:20 -040020import android.app.NotificationManager.Policy;
John Spurlock3b98b3f2014-05-01 09:08:48 -040021import android.content.ComponentName;
John Spurlockc90e6fe2014-10-28 11:21:42 -040022import android.content.Context;
Jason Monk4dd81462014-09-08 10:13:59 -040023import android.content.res.Resources;
John Spurlock3b98b3f2014-05-01 09:08:48 -040024import android.net.Uri;
John Spurlock056c5192014-04-20 21:52:01 -040025import android.os.Parcel;
26import android.os.Parcelable;
John Spurlock1b8b22b2015-05-20 09:47:13 -040027import android.os.UserHandle;
John Spurlockb2278d62015-04-07 12:47:12 -040028import android.provider.Settings.Global;
John Spurlock056c5192014-04-20 21:52:01 -040029import android.text.TextUtils;
John Spurlockc90e6fe2014-10-28 11:21:42 -040030import android.text.format.DateFormat;
John Spurlockb2278d62015-04-07 12:47:12 -040031import android.util.ArrayMap;
32import android.util.ArraySet;
John Spurlock856edeb2014-06-01 20:36:47 -040033import android.util.Slog;
John Spurlock056c5192014-04-20 21:52:01 -040034
John Spurlockb2278d62015-04-07 12:47:12 -040035import com.android.internal.R;
36
John Spurlock056c5192014-04-20 21:52:01 -040037import org.xmlpull.v1.XmlPullParser;
38import org.xmlpull.v1.XmlPullParserException;
39import org.xmlpull.v1.XmlSerializer;
40
41import java.io.IOException;
John Spurlock3b98b3f2014-05-01 09:08:48 -040042import java.util.ArrayList;
Julia Reynolds7f733082016-04-26 14:19:19 -040043import java.util.Arrays;
John Spurlockae641c92014-06-30 18:11:40 -040044import java.util.Calendar;
Julia Reynolds8e2d2bb2015-10-01 14:34:22 -040045import java.util.GregorianCalendar;
John Spurlockc90e6fe2014-10-28 11:21:42 -040046import java.util.Locale;
John Spurlock056c5192014-04-20 21:52:01 -040047import java.util.Objects;
John Spurlockb2278d62015-04-07 12:47:12 -040048import java.util.UUID;
John Spurlockc90e6fe2014-10-28 11:21:42 -040049
John Spurlock056c5192014-04-20 21:52:01 -040050/**
51 * Persisted configuration for zen mode.
52 *
53 * @hide
54 */
55public class ZenModeConfig implements Parcelable {
John Spurlock856edeb2014-06-01 20:36:47 -040056 private static String TAG = "ZenModeConfig";
John Spurlock056c5192014-04-20 21:52:01 -040057
Chris Wren99f963e2014-05-28 16:52:42 -040058 public static final int SOURCE_ANYONE = 0;
59 public static final int SOURCE_CONTACT = 1;
60 public static final int SOURCE_STAR = 2;
61 public static final int MAX_SOURCE = SOURCE_STAR;
John Spurlocka492d1d2015-05-05 18:30:28 -040062 private static final int DEFAULT_SOURCE = SOURCE_CONTACT;
Chris Wren99f963e2014-05-28 16:52:42 -040063
John Spurlockae641c92014-06-30 18:11:40 -040064 public static final int[] ALL_DAYS = { Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY,
65 Calendar.WEDNESDAY, Calendar.THURSDAY, Calendar.FRIDAY, Calendar.SATURDAY };
66 public static final int[] WEEKNIGHT_DAYS = { Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY,
67 Calendar.WEDNESDAY, Calendar.THURSDAY };
John Spurlockb2278d62015-04-07 12:47:12 -040068 public static final int[] WEEKEND_DAYS = { Calendar.FRIDAY, Calendar.SATURDAY };
John Spurlockae641c92014-06-30 18:11:40 -040069
John Spurlockd9c75db2015-04-28 11:19:13 -040070 public static final int[] MINUTE_BUCKETS = generateMinuteBuckets();
Jason Monk4dd81462014-09-08 10:13:59 -040071 private static final int SECONDS_MS = 1000;
72 private static final int MINUTES_MS = 60 * SECONDS_MS;
Julia Reynolds0842fe82015-10-01 14:34:22 -040073 private static final int DAY_MINUTES = 24 * 60;
John Spurlock530052a2014-11-30 16:26:19 -050074 private static final int ZERO_VALUE_MS = 10 * SECONDS_MS;
Jason Monk4dd81462014-09-08 10:13:59 -040075
John Spurlocka492d1d2015-05-05 18:30:28 -040076 private static final boolean DEFAULT_ALLOW_CALLS = true;
77 private static final boolean DEFAULT_ALLOW_MESSAGES = false;
John Spurlockfc746f82015-04-03 13:47:14 -040078 private static final boolean DEFAULT_ALLOW_REMINDERS = true;
John Spurlock2dac62c2014-09-19 15:29:06 -040079 private static final boolean DEFAULT_ALLOW_EVENTS = true;
John Spurlock1d7d2242015-04-10 08:10:22 -040080 private static final boolean DEFAULT_ALLOW_REPEAT_CALLERS = false;
Julia Reynoldsd5607292016-02-05 15:25:58 -050081 private static final boolean DEFAULT_ALLOW_SCREEN_OFF = true;
Julia Reynolds61721582016-01-05 08:35:25 -050082 private static final boolean DEFAULT_ALLOW_SCREEN_ON = true;
John Spurlock2dac62c2014-09-19 15:29:06 -040083
John Spurlockb2278d62015-04-07 12:47:12 -040084 private static final int XML_VERSION = 2;
John Spurlock056c5192014-04-20 21:52:01 -040085 private static final String ZEN_TAG = "zen";
86 private static final String ZEN_ATT_VERSION = "version";
John Spurlock21258a32015-05-27 18:22:55 -040087 private static final String ZEN_ATT_USER = "user";
John Spurlock056c5192014-04-20 21:52:01 -040088 private static final String ALLOW_TAG = "allow";
89 private static final String ALLOW_ATT_CALLS = "calls";
John Spurlock1d7d2242015-04-10 08:10:22 -040090 private static final String ALLOW_ATT_REPEAT_CALLERS = "repeatCallers";
John Spurlock056c5192014-04-20 21:52:01 -040091 private static final String ALLOW_ATT_MESSAGES = "messages";
Chris Wren99f963e2014-05-28 16:52:42 -040092 private static final String ALLOW_ATT_FROM = "from";
John Spurlocka492d1d2015-05-05 18:30:28 -040093 private static final String ALLOW_ATT_CALLS_FROM = "callsFrom";
94 private static final String ALLOW_ATT_MESSAGES_FROM = "messagesFrom";
John Spurlockfc746f82015-04-03 13:47:14 -040095 private static final String ALLOW_ATT_REMINDERS = "reminders";
John Spurlock2dac62c2014-09-19 15:29:06 -040096 private static final String ALLOW_ATT_EVENTS = "events";
Julia Reynoldsd5607292016-02-05 15:25:58 -050097 private static final String ALLOW_ATT_SCREEN_OFF = "visualScreenOff";
98 private static final String ALLOW_ATT_SCREEN_ON = "visualScreenOn";
John Spurlock056c5192014-04-20 21:52:01 -040099
John Spurlock3b98b3f2014-05-01 09:08:48 -0400100 private static final String CONDITION_TAG = "condition";
101 private static final String CONDITION_ATT_COMPONENT = "component";
102 private static final String CONDITION_ATT_ID = "id";
John Spurlock4db0d982014-08-13 09:19:03 -0400103 private static final String CONDITION_ATT_SUMMARY = "summary";
104 private static final String CONDITION_ATT_LINE1 = "line1";
105 private static final String CONDITION_ATT_LINE2 = "line2";
106 private static final String CONDITION_ATT_ICON = "icon";
107 private static final String CONDITION_ATT_STATE = "state";
108 private static final String CONDITION_ATT_FLAGS = "flags";
John Spurlock3b98b3f2014-05-01 09:08:48 -0400109
John Spurlockb2278d62015-04-07 12:47:12 -0400110 private static final String MANUAL_TAG = "manual";
111 private static final String AUTOMATIC_TAG = "automatic";
112
John Spurlockd60258f2015-04-30 09:30:52 -0400113 private static final String RULE_ATT_ID = "ruleId";
John Spurlockb2278d62015-04-07 12:47:12 -0400114 private static final String RULE_ATT_ENABLED = "enabled";
115 private static final String RULE_ATT_SNOOZING = "snoozing";
116 private static final String RULE_ATT_NAME = "name";
117 private static final String RULE_ATT_COMPONENT = "component";
118 private static final String RULE_ATT_ZEN = "zen";
119 private static final String RULE_ATT_CONDITION_ID = "conditionId";
Julia Reynolds56106ff2015-09-30 14:42:53 -0400120 private static final String RULE_ATT_CREATION_TIME = "creationTime";
John Spurlock856edeb2014-06-01 20:36:47 -0400121
John Spurlocka492d1d2015-05-05 18:30:28 -0400122 public boolean allowCalls = DEFAULT_ALLOW_CALLS;
John Spurlock1d7d2242015-04-10 08:10:22 -0400123 public boolean allowRepeatCallers = DEFAULT_ALLOW_REPEAT_CALLERS;
John Spurlocka492d1d2015-05-05 18:30:28 -0400124 public boolean allowMessages = DEFAULT_ALLOW_MESSAGES;
John Spurlockfc746f82015-04-03 13:47:14 -0400125 public boolean allowReminders = DEFAULT_ALLOW_REMINDERS;
John Spurlock2dac62c2014-09-19 15:29:06 -0400126 public boolean allowEvents = DEFAULT_ALLOW_EVENTS;
John Spurlocka492d1d2015-05-05 18:30:28 -0400127 public int allowCallsFrom = DEFAULT_SOURCE;
128 public int allowMessagesFrom = DEFAULT_SOURCE;
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -0700129 public int user = UserHandle.USER_SYSTEM;
Julia Reynoldsd5607292016-02-05 15:25:58 -0500130 public boolean allowWhenScreenOff = DEFAULT_ALLOW_SCREEN_OFF;
131 public boolean allowWhenScreenOn = DEFAULT_ALLOW_SCREEN_ON;
John Spurlock056c5192014-04-20 21:52:01 -0400132
John Spurlockb2278d62015-04-07 12:47:12 -0400133 public ZenRule manualRule;
134 public ArrayMap<String, ZenRule> automaticRules = new ArrayMap<>();
John Spurlock056c5192014-04-20 21:52:01 -0400135
136 public ZenModeConfig() { }
137
138 public ZenModeConfig(Parcel source) {
139 allowCalls = source.readInt() == 1;
John Spurlock1d7d2242015-04-10 08:10:22 -0400140 allowRepeatCallers = source.readInt() == 1;
John Spurlock056c5192014-04-20 21:52:01 -0400141 allowMessages = source.readInt() == 1;
John Spurlockfc746f82015-04-03 13:47:14 -0400142 allowReminders = source.readInt() == 1;
John Spurlock2dac62c2014-09-19 15:29:06 -0400143 allowEvents = source.readInt() == 1;
John Spurlocka492d1d2015-05-05 18:30:28 -0400144 allowCallsFrom = source.readInt();
145 allowMessagesFrom = source.readInt();
John Spurlock21258a32015-05-27 18:22:55 -0400146 user = source.readInt();
John Spurlockb2278d62015-04-07 12:47:12 -0400147 manualRule = source.readParcelable(null);
148 final int len = source.readInt();
149 if (len > 0) {
150 final String[] ids = new String[len];
151 final ZenRule[] rules = new ZenRule[len];
152 source.readStringArray(ids);
153 source.readTypedArray(rules, ZenRule.CREATOR);
154 for (int i = 0; i < len; i++) {
155 automaticRules.put(ids[i], rules[i]);
156 }
157 }
Julia Reynoldsd5607292016-02-05 15:25:58 -0500158 allowWhenScreenOff = source.readInt() == 1;
159 allowWhenScreenOn = source.readInt() == 1;
John Spurlock056c5192014-04-20 21:52:01 -0400160 }
161
162 @Override
163 public void writeToParcel(Parcel dest, int flags) {
164 dest.writeInt(allowCalls ? 1 : 0);
John Spurlock1d7d2242015-04-10 08:10:22 -0400165 dest.writeInt(allowRepeatCallers ? 1 : 0);
John Spurlock056c5192014-04-20 21:52:01 -0400166 dest.writeInt(allowMessages ? 1 : 0);
John Spurlockfc746f82015-04-03 13:47:14 -0400167 dest.writeInt(allowReminders ? 1 : 0);
John Spurlock2dac62c2014-09-19 15:29:06 -0400168 dest.writeInt(allowEvents ? 1 : 0);
John Spurlocka492d1d2015-05-05 18:30:28 -0400169 dest.writeInt(allowCallsFrom);
170 dest.writeInt(allowMessagesFrom);
John Spurlock21258a32015-05-27 18:22:55 -0400171 dest.writeInt(user);
John Spurlockb2278d62015-04-07 12:47:12 -0400172 dest.writeParcelable(manualRule, 0);
173 if (!automaticRules.isEmpty()) {
174 final int len = automaticRules.size();
175 final String[] ids = new String[len];
176 final ZenRule[] rules = new ZenRule[len];
177 for (int i = 0; i < len; i++) {
178 ids[i] = automaticRules.keyAt(i);
179 rules[i] = automaticRules.valueAt(i);
180 }
181 dest.writeInt(len);
182 dest.writeStringArray(ids);
183 dest.writeTypedArray(rules, 0);
184 } else {
185 dest.writeInt(0);
186 }
Julia Reynoldsd5607292016-02-05 15:25:58 -0500187 dest.writeInt(allowWhenScreenOff ? 1 : 0);
188 dest.writeInt(allowWhenScreenOn ? 1 : 0);
John Spurlock056c5192014-04-20 21:52:01 -0400189 }
190
191 @Override
192 public String toString() {
193 return new StringBuilder(ZenModeConfig.class.getSimpleName()).append('[')
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500194 .append("user=").append(user)
195 .append(",allowCalls=").append(allowCalls)
196 .append(",allowRepeatCallers=").append(allowRepeatCallers)
197 .append(",allowMessages=").append(allowMessages)
198 .append(",allowCallsFrom=").append(sourceToString(allowCallsFrom))
199 .append(",allowMessagesFrom=").append(sourceToString(allowMessagesFrom))
200 .append(",allowReminders=").append(allowReminders)
201 .append(",allowEvents=").append(allowEvents)
Julia Reynoldsd5607292016-02-05 15:25:58 -0500202 .append(",allowWhenScreenOff=").append(allowWhenScreenOff)
203 .append(",allowWhenScreenOn=").append(allowWhenScreenOn)
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500204 .append(",automaticRules=").append(automaticRules)
205 .append(",manualRule=").append(manualRule)
206 .append(']').toString();
John Spurlock056c5192014-04-20 21:52:01 -0400207 }
208
John Spurlock21258a32015-05-27 18:22:55 -0400209 private Diff diff(ZenModeConfig to) {
210 final Diff d = new Diff();
211 if (to == null) {
212 return d.addLine("config", "delete");
213 }
214 if (user != to.user) {
215 d.addLine("user", user, to.user);
216 }
217 if (allowCalls != to.allowCalls) {
218 d.addLine("allowCalls", allowCalls, to.allowCalls);
219 }
220 if (allowRepeatCallers != to.allowRepeatCallers) {
221 d.addLine("allowRepeatCallers", allowRepeatCallers, to.allowRepeatCallers);
222 }
223 if (allowMessages != to.allowMessages) {
224 d.addLine("allowMessages", allowMessages, to.allowMessages);
225 }
226 if (allowCallsFrom != to.allowCallsFrom) {
227 d.addLine("allowCallsFrom", allowCallsFrom, to.allowCallsFrom);
228 }
229 if (allowMessagesFrom != to.allowMessagesFrom) {
230 d.addLine("allowMessagesFrom", allowMessagesFrom, to.allowMessagesFrom);
231 }
232 if (allowReminders != to.allowReminders) {
233 d.addLine("allowReminders", allowReminders, to.allowReminders);
234 }
235 if (allowEvents != to.allowEvents) {
236 d.addLine("allowEvents", allowEvents, to.allowEvents);
237 }
Julia Reynoldsd5607292016-02-05 15:25:58 -0500238 if (allowWhenScreenOff != to.allowWhenScreenOff) {
239 d.addLine("allowWhenScreenOff", allowWhenScreenOff, to.allowWhenScreenOff);
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500240 }
Julia Reynoldsd5607292016-02-05 15:25:58 -0500241 if (allowWhenScreenOn != to.allowWhenScreenOn) {
242 d.addLine("allowWhenScreenOn", allowWhenScreenOn, to.allowWhenScreenOn);
Julia Reynolds61721582016-01-05 08:35:25 -0500243 }
John Spurlock21258a32015-05-27 18:22:55 -0400244 final ArraySet<String> allRules = new ArraySet<>();
245 addKeys(allRules, automaticRules);
246 addKeys(allRules, to.automaticRules);
247 final int N = allRules.size();
248 for (int i = 0; i < N; i++) {
249 final String rule = allRules.valueAt(i);
250 final ZenRule fromRule = automaticRules != null ? automaticRules.get(rule) : null;
251 final ZenRule toRule = to.automaticRules != null ? to.automaticRules.get(rule) : null;
252 ZenRule.appendDiff(d, "automaticRule[" + rule + "]", fromRule, toRule);
253 }
254 ZenRule.appendDiff(d, "manualRule", manualRule, to.manualRule);
255 return d;
256 }
257
258 public static Diff diff(ZenModeConfig from, ZenModeConfig to) {
259 if (from == null) {
260 final Diff d = new Diff();
261 if (to != null) {
262 d.addLine("config", "insert");
263 }
264 return d;
265 }
266 return from.diff(to);
267 }
268
269 private static <T> void addKeys(ArraySet<T> set, ArrayMap<T, ?> map) {
270 if (map != null) {
271 for (int i = 0; i < map.size(); i++) {
272 set.add(map.keyAt(i));
273 }
274 }
275 }
276
John Spurlockb2278d62015-04-07 12:47:12 -0400277 public boolean isValid() {
278 if (!isValidManualRule(manualRule)) return false;
279 final int N = automaticRules.size();
280 for (int i = 0; i < N; i++) {
281 if (!isValidAutomaticRule(automaticRules.valueAt(i))) return false;
282 }
283 return true;
284 }
285
286 private static boolean isValidManualRule(ZenRule rule) {
287 return rule == null || Global.isValidZenMode(rule.zenMode) && sameCondition(rule);
288 }
289
290 private static boolean isValidAutomaticRule(ZenRule rule) {
291 return rule != null && !TextUtils.isEmpty(rule.name) && Global.isValidZenMode(rule.zenMode)
292 && rule.conditionId != null && sameCondition(rule);
293 }
294
295 private static boolean sameCondition(ZenRule rule) {
296 if (rule == null) return false;
297 if (rule.conditionId == null) {
298 return rule.condition == null;
299 } else {
300 return rule.condition == null || rule.conditionId.equals(rule.condition.id);
301 }
302 }
303
John Spurlockd9c75db2015-04-28 11:19:13 -0400304 private static int[] generateMinuteBuckets() {
305 final int maxHrs = 12;
306 final int[] buckets = new int[maxHrs + 3];
307 buckets[0] = 15;
308 buckets[1] = 30;
309 buckets[2] = 45;
310 for (int i = 1; i <= maxHrs; i++) {
311 buckets[2 + i] = 60 * i;
312 }
313 return buckets;
314 }
315
Chris Wren99f963e2014-05-28 16:52:42 -0400316 public static String sourceToString(int source) {
317 switch (source) {
318 case SOURCE_ANYONE:
319 return "anyone";
320 case SOURCE_CONTACT:
321 return "contacts";
322 case SOURCE_STAR:
323 return "stars";
324 default:
325 return "UNKNOWN";
326 }
327 }
328
John Spurlock056c5192014-04-20 21:52:01 -0400329 @Override
330 public boolean equals(Object o) {
331 if (!(o instanceof ZenModeConfig)) return false;
332 if (o == this) return true;
333 final ZenModeConfig other = (ZenModeConfig) o;
334 return other.allowCalls == allowCalls
John Spurlock1d7d2242015-04-10 08:10:22 -0400335 && other.allowRepeatCallers == allowRepeatCallers
John Spurlock056c5192014-04-20 21:52:01 -0400336 && other.allowMessages == allowMessages
John Spurlocka492d1d2015-05-05 18:30:28 -0400337 && other.allowCallsFrom == allowCallsFrom
338 && other.allowMessagesFrom == allowMessagesFrom
John Spurlockfc746f82015-04-03 13:47:14 -0400339 && other.allowReminders == allowReminders
John Spurlock2dac62c2014-09-19 15:29:06 -0400340 && other.allowEvents == allowEvents
Julia Reynoldsd5607292016-02-05 15:25:58 -0500341 && other.allowWhenScreenOff == allowWhenScreenOff
342 && other.allowWhenScreenOn == allowWhenScreenOn
John Spurlock21258a32015-05-27 18:22:55 -0400343 && other.user == user
John Spurlockb2278d62015-04-07 12:47:12 -0400344 && Objects.equals(other.automaticRules, automaticRules)
345 && Objects.equals(other.manualRule, manualRule);
John Spurlock056c5192014-04-20 21:52:01 -0400346 }
347
348 @Override
349 public int hashCode() {
John Spurlocka492d1d2015-05-05 18:30:28 -0400350 return Objects.hash(allowCalls, allowRepeatCallers, allowMessages, allowCallsFrom,
Julia Reynoldsd5607292016-02-05 15:25:58 -0500351 allowMessagesFrom, allowReminders, allowEvents, allowWhenScreenOff,
352 allowWhenScreenOn,
353 user, automaticRules, manualRule);
John Spurlock056c5192014-04-20 21:52:01 -0400354 }
355
John Spurlockb2278d62015-04-07 12:47:12 -0400356 private static String toDayList(int[] days) {
357 if (days == null || days.length == 0) return "";
358 final StringBuilder sb = new StringBuilder();
359 for (int i = 0; i < days.length; i++) {
360 if (i > 0) sb.append('.');
361 sb.append(days[i]);
362 }
363 return sb.toString();
John Spurlockae641c92014-06-30 18:11:40 -0400364 }
365
John Spurlockb2278d62015-04-07 12:47:12 -0400366 private static int[] tryParseDayList(String dayList, String sep) {
367 if (dayList == null) return null;
368 final String[] tokens = dayList.split(sep);
John Spurlockae641c92014-06-30 18:11:40 -0400369 if (tokens.length == 0) return null;
370 final int[] rt = new int[tokens.length];
371 for (int i = 0; i < tokens.length; i++) {
372 final int day = tryParseInt(tokens[i], -1);
373 if (day == -1) return null;
374 rt[i] = day;
375 }
376 return rt;
377 }
378
379 private static int tryParseInt(String value, int defValue) {
380 if (TextUtils.isEmpty(value)) return defValue;
381 try {
Narayan Kamatha09b4d22016-04-15 18:32:45 +0100382 return Integer.parseInt(value);
John Spurlockae641c92014-06-30 18:11:40 -0400383 } catch (NumberFormatException e) {
384 return defValue;
385 }
John Spurlock056c5192014-04-20 21:52:01 -0400386 }
387
John Spurlockd60258f2015-04-30 09:30:52 -0400388 private static long tryParseLong(String value, long defValue) {
389 if (TextUtils.isEmpty(value)) return defValue;
390 try {
391 return Long.valueOf(value);
392 } catch (NumberFormatException e) {
393 return defValue;
394 }
395 }
396
John Spurlockb2278d62015-04-07 12:47:12 -0400397 public static ZenModeConfig readXml(XmlPullParser parser, Migration migration)
John Spurlock056c5192014-04-20 21:52:01 -0400398 throws XmlPullParserException, IOException {
399 int type = parser.getEventType();
400 if (type != XmlPullParser.START_TAG) return null;
401 String tag = parser.getName();
402 if (!ZEN_TAG.equals(tag)) return null;
403 final ZenModeConfig rt = new ZenModeConfig();
John Spurlockae641c92014-06-30 18:11:40 -0400404 final int version = safeInt(parser, ZEN_ATT_VERSION, XML_VERSION);
John Spurlockb2278d62015-04-07 12:47:12 -0400405 if (version == 1) {
406 final XmlV1 v1 = XmlV1.readXml(parser);
407 return migration.migrate(v1);
408 }
John Spurlock21258a32015-05-27 18:22:55 -0400409 rt.user = safeInt(parser, ZEN_ATT_USER, rt.user);
John Spurlock056c5192014-04-20 21:52:01 -0400410 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
411 tag = parser.getName();
John Spurlock3b98b3f2014-05-01 09:08:48 -0400412 if (type == XmlPullParser.END_TAG && ZEN_TAG.equals(tag)) {
John Spurlock3b98b3f2014-05-01 09:08:48 -0400413 return rt;
414 }
John Spurlock056c5192014-04-20 21:52:01 -0400415 if (type == XmlPullParser.START_TAG) {
416 if (ALLOW_TAG.equals(tag)) {
417 rt.allowCalls = safeBoolean(parser, ALLOW_ATT_CALLS, false);
John Spurlock1d7d2242015-04-10 08:10:22 -0400418 rt.allowRepeatCallers = safeBoolean(parser, ALLOW_ATT_REPEAT_CALLERS,
419 DEFAULT_ALLOW_REPEAT_CALLERS);
John Spurlock056c5192014-04-20 21:52:01 -0400420 rt.allowMessages = safeBoolean(parser, ALLOW_ATT_MESSAGES, false);
John Spurlockfc746f82015-04-03 13:47:14 -0400421 rt.allowReminders = safeBoolean(parser, ALLOW_ATT_REMINDERS,
422 DEFAULT_ALLOW_REMINDERS);
John Spurlock2dac62c2014-09-19 15:29:06 -0400423 rt.allowEvents = safeBoolean(parser, ALLOW_ATT_EVENTS, DEFAULT_ALLOW_EVENTS);
John Spurlocka492d1d2015-05-05 18:30:28 -0400424 final int from = safeInt(parser, ALLOW_ATT_FROM, -1);
425 final int callsFrom = safeInt(parser, ALLOW_ATT_CALLS_FROM, -1);
426 final int messagesFrom = safeInt(parser, ALLOW_ATT_MESSAGES_FROM, -1);
427 if (isValidSource(callsFrom) && isValidSource(messagesFrom)) {
428 rt.allowCallsFrom = callsFrom;
429 rt.allowMessagesFrom = messagesFrom;
430 } else if (isValidSource(from)) {
431 Slog.i(TAG, "Migrating existing shared 'from': " + sourceToString(from));
432 rt.allowCallsFrom = from;
433 rt.allowMessagesFrom = from;
434 } else {
435 rt.allowCallsFrom = DEFAULT_SOURCE;
436 rt.allowMessagesFrom = DEFAULT_SOURCE;
Chris Wren99f963e2014-05-28 16:52:42 -0400437 }
Julia Reynoldsd5607292016-02-05 15:25:58 -0500438 rt.allowWhenScreenOff =
439 safeBoolean(parser, ALLOW_ATT_SCREEN_OFF, DEFAULT_ALLOW_SCREEN_OFF);
440 rt.allowWhenScreenOn =
Julia Reynolds61721582016-01-05 08:35:25 -0500441 safeBoolean(parser, ALLOW_ATT_SCREEN_ON, DEFAULT_ALLOW_SCREEN_ON);
John Spurlockb2278d62015-04-07 12:47:12 -0400442 } else if (MANUAL_TAG.equals(tag)) {
John Spurlock995a7492015-05-28 22:13:03 -0400443 rt.manualRule = readRuleXml(parser);
John Spurlockb2278d62015-04-07 12:47:12 -0400444 } else if (AUTOMATIC_TAG.equals(tag)) {
445 final String id = parser.getAttributeValue(null, RULE_ATT_ID);
John Spurlock995a7492015-05-28 22:13:03 -0400446 final ZenRule automaticRule = readRuleXml(parser);
John Spurlockb2278d62015-04-07 12:47:12 -0400447 if (id != null && automaticRule != null) {
Julia Reynolds56106ff2015-09-30 14:42:53 -0400448 automaticRule.id = id;
John Spurlockb2278d62015-04-07 12:47:12 -0400449 rt.automaticRules.put(id, automaticRule);
John Spurlock4db0d982014-08-13 09:19:03 -0400450 }
John Spurlock056c5192014-04-20 21:52:01 -0400451 }
452 }
453 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400454 throw new IllegalStateException("Failed to reach END_DOCUMENT");
John Spurlock056c5192014-04-20 21:52:01 -0400455 }
456
457 public void writeXml(XmlSerializer out) throws IOException {
458 out.startTag(null, ZEN_TAG);
459 out.attribute(null, ZEN_ATT_VERSION, Integer.toString(XML_VERSION));
John Spurlock21258a32015-05-27 18:22:55 -0400460 out.attribute(null, ZEN_ATT_USER, Integer.toString(user));
John Spurlock056c5192014-04-20 21:52:01 -0400461
462 out.startTag(null, ALLOW_TAG);
463 out.attribute(null, ALLOW_ATT_CALLS, Boolean.toString(allowCalls));
John Spurlock1d7d2242015-04-10 08:10:22 -0400464 out.attribute(null, ALLOW_ATT_REPEAT_CALLERS, Boolean.toString(allowRepeatCallers));
John Spurlock056c5192014-04-20 21:52:01 -0400465 out.attribute(null, ALLOW_ATT_MESSAGES, Boolean.toString(allowMessages));
John Spurlockfc746f82015-04-03 13:47:14 -0400466 out.attribute(null, ALLOW_ATT_REMINDERS, Boolean.toString(allowReminders));
John Spurlock2dac62c2014-09-19 15:29:06 -0400467 out.attribute(null, ALLOW_ATT_EVENTS, Boolean.toString(allowEvents));
John Spurlocka492d1d2015-05-05 18:30:28 -0400468 out.attribute(null, ALLOW_ATT_CALLS_FROM, Integer.toString(allowCallsFrom));
469 out.attribute(null, ALLOW_ATT_MESSAGES_FROM, Integer.toString(allowMessagesFrom));
Julia Reynoldsd5607292016-02-05 15:25:58 -0500470 out.attribute(null, ALLOW_ATT_SCREEN_OFF, Boolean.toString(allowWhenScreenOff));
471 out.attribute(null, ALLOW_ATT_SCREEN_ON, Boolean.toString(allowWhenScreenOn));
John Spurlock056c5192014-04-20 21:52:01 -0400472 out.endTag(null, ALLOW_TAG);
473
John Spurlockb2278d62015-04-07 12:47:12 -0400474 if (manualRule != null) {
475 out.startTag(null, MANUAL_TAG);
476 writeRuleXml(manualRule, out);
477 out.endTag(null, MANUAL_TAG);
John Spurlock056c5192014-04-20 21:52:01 -0400478 }
John Spurlockb2278d62015-04-07 12:47:12 -0400479 final int N = automaticRules.size();
480 for (int i = 0; i < N; i++) {
481 final String id = automaticRules.keyAt(i);
482 final ZenRule automaticRule = automaticRules.valueAt(i);
483 out.startTag(null, AUTOMATIC_TAG);
484 out.attribute(null, RULE_ATT_ID, id);
485 writeRuleXml(automaticRule, out);
486 out.endTag(null, AUTOMATIC_TAG);
John Spurlock856edeb2014-06-01 20:36:47 -0400487 }
John Spurlock056c5192014-04-20 21:52:01 -0400488 out.endTag(null, ZEN_TAG);
489 }
490
John Spurlock995a7492015-05-28 22:13:03 -0400491 public static ZenRule readRuleXml(XmlPullParser parser) {
John Spurlockb2278d62015-04-07 12:47:12 -0400492 final ZenRule rt = new ZenRule();
493 rt.enabled = safeBoolean(parser, RULE_ATT_ENABLED, true);
494 rt.snoozing = safeBoolean(parser, RULE_ATT_SNOOZING, false);
495 rt.name = parser.getAttributeValue(null, RULE_ATT_NAME);
496 final String zen = parser.getAttributeValue(null, RULE_ATT_ZEN);
497 rt.zenMode = tryParseZenMode(zen, -1);
498 if (rt.zenMode == -1) {
499 Slog.w(TAG, "Bad zen mode in rule xml:" + zen);
500 return null;
501 }
502 rt.conditionId = safeUri(parser, RULE_ATT_CONDITION_ID);
503 rt.component = safeComponentName(parser, RULE_ATT_COMPONENT);
Julia Reynolds56106ff2015-09-30 14:42:53 -0400504 rt.creationTime = safeLong(parser, RULE_ATT_CREATION_TIME, 0);
John Spurlockb2278d62015-04-07 12:47:12 -0400505 rt.condition = readConditionXml(parser);
John Spurlockd60258f2015-04-30 09:30:52 -0400506 return rt;
John Spurlockb2278d62015-04-07 12:47:12 -0400507 }
508
509 public static void writeRuleXml(ZenRule rule, XmlSerializer out) throws IOException {
510 out.attribute(null, RULE_ATT_ENABLED, Boolean.toString(rule.enabled));
511 out.attribute(null, RULE_ATT_SNOOZING, Boolean.toString(rule.snoozing));
512 if (rule.name != null) {
513 out.attribute(null, RULE_ATT_NAME, rule.name);
514 }
515 out.attribute(null, RULE_ATT_ZEN, Integer.toString(rule.zenMode));
516 if (rule.component != null) {
517 out.attribute(null, RULE_ATT_COMPONENT, rule.component.flattenToString());
518 }
519 if (rule.conditionId != null) {
520 out.attribute(null, RULE_ATT_CONDITION_ID, rule.conditionId.toString());
521 }
Julia Reynolds56106ff2015-09-30 14:42:53 -0400522 out.attribute(null, RULE_ATT_CREATION_TIME, Long.toString(rule.creationTime));
John Spurlockb2278d62015-04-07 12:47:12 -0400523 if (rule.condition != null) {
524 writeConditionXml(rule.condition, out);
525 }
526 }
527
John Spurlock4db0d982014-08-13 09:19:03 -0400528 public static Condition readConditionXml(XmlPullParser parser) {
529 final Uri id = safeUri(parser, CONDITION_ATT_ID);
John Spurlockb2278d62015-04-07 12:47:12 -0400530 if (id == null) return null;
John Spurlock4db0d982014-08-13 09:19:03 -0400531 final String summary = parser.getAttributeValue(null, CONDITION_ATT_SUMMARY);
532 final String line1 = parser.getAttributeValue(null, CONDITION_ATT_LINE1);
533 final String line2 = parser.getAttributeValue(null, CONDITION_ATT_LINE2);
534 final int icon = safeInt(parser, CONDITION_ATT_ICON, -1);
535 final int state = safeInt(parser, CONDITION_ATT_STATE, -1);
536 final int flags = safeInt(parser, CONDITION_ATT_FLAGS, -1);
537 try {
538 return new Condition(id, summary, line1, line2, icon, state, flags);
539 } catch (IllegalArgumentException e) {
540 Slog.w(TAG, "Unable to read condition xml", e);
541 return null;
542 }
543 }
544
545 public static void writeConditionXml(Condition c, XmlSerializer out) throws IOException {
546 out.attribute(null, CONDITION_ATT_ID, c.id.toString());
547 out.attribute(null, CONDITION_ATT_SUMMARY, c.summary);
548 out.attribute(null, CONDITION_ATT_LINE1, c.line1);
549 out.attribute(null, CONDITION_ATT_LINE2, c.line2);
550 out.attribute(null, CONDITION_ATT_ICON, Integer.toString(c.icon));
551 out.attribute(null, CONDITION_ATT_STATE, Integer.toString(c.state));
552 out.attribute(null, CONDITION_ATT_FLAGS, Integer.toString(c.flags));
553 }
554
John Spurlock056c5192014-04-20 21:52:01 -0400555 public static boolean isValidHour(int val) {
556 return val >= 0 && val < 24;
557 }
558
559 public static boolean isValidMinute(int val) {
560 return val >= 0 && val < 60;
561 }
562
John Spurlocka492d1d2015-05-05 18:30:28 -0400563 private static boolean isValidSource(int source) {
564 return source >= SOURCE_ANYONE && source <= MAX_SOURCE;
565 }
566
John Spurlock056c5192014-04-20 21:52:01 -0400567 private static boolean safeBoolean(XmlPullParser parser, String att, boolean defValue) {
568 final String val = parser.getAttributeValue(null, att);
Julia Reynolds1998ee52016-02-11 13:49:08 -0500569 return safeBoolean(val, defValue);
570 }
571
572 private static boolean safeBoolean(String val, boolean defValue) {
John Spurlock056c5192014-04-20 21:52:01 -0400573 if (TextUtils.isEmpty(val)) return defValue;
574 return Boolean.valueOf(val);
575 }
576
577 private static int safeInt(XmlPullParser parser, String att, int defValue) {
578 final String val = parser.getAttributeValue(null, att);
John Spurlockae641c92014-06-30 18:11:40 -0400579 return tryParseInt(val, defValue);
John Spurlock056c5192014-04-20 21:52:01 -0400580 }
581
John Spurlock3b98b3f2014-05-01 09:08:48 -0400582 private static ComponentName safeComponentName(XmlPullParser parser, String att) {
583 final String val = parser.getAttributeValue(null, att);
584 if (TextUtils.isEmpty(val)) return null;
585 return ComponentName.unflattenFromString(val);
586 }
587
588 private static Uri safeUri(XmlPullParser parser, String att) {
589 final String val = parser.getAttributeValue(null, att);
590 if (TextUtils.isEmpty(val)) return null;
591 return Uri.parse(val);
592 }
593
Julia Reynolds56106ff2015-09-30 14:42:53 -0400594 private static long safeLong(XmlPullParser parser, String att, long defValue) {
595 final String val = parser.getAttributeValue(null, att);
596 return tryParseLong(val, defValue);
597 }
598
John Spurlock056c5192014-04-20 21:52:01 -0400599 @Override
600 public int describeContents() {
601 return 0;
602 }
603
604 public ZenModeConfig copy() {
605 final Parcel parcel = Parcel.obtain();
606 try {
607 writeToParcel(parcel, 0);
608 parcel.setDataPosition(0);
609 return new ZenModeConfig(parcel);
610 } finally {
611 parcel.recycle();
612 }
613 }
614
615 public static final Parcelable.Creator<ZenModeConfig> CREATOR
616 = new Parcelable.Creator<ZenModeConfig>() {
617 @Override
618 public ZenModeConfig createFromParcel(Parcel source) {
619 return new ZenModeConfig(source);
620 }
621
622 @Override
623 public ZenModeConfig[] newArray(int size) {
624 return new ZenModeConfig[size];
625 }
626 };
John Spurlock856edeb2014-06-01 20:36:47 -0400627
John Spurlock1fc476d2015-04-14 16:05:20 -0400628 public Policy toNotificationPolicy() {
629 int priorityCategories = 0;
John Spurlocka492d1d2015-05-05 18:30:28 -0400630 int priorityCallSenders = Policy.PRIORITY_SENDERS_CONTACTS;
631 int priorityMessageSenders = Policy.PRIORITY_SENDERS_CONTACTS;
John Spurlock1fc476d2015-04-14 16:05:20 -0400632 if (allowCalls) {
633 priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS;
634 }
635 if (allowMessages) {
636 priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES;
637 }
638 if (allowEvents) {
639 priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS;
640 }
641 if (allowReminders) {
642 priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS;
643 }
644 if (allowRepeatCallers) {
645 priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS;
646 }
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500647 int suppressedVisualEffects = 0;
Julia Reynoldsd5607292016-02-05 15:25:58 -0500648 if (!allowWhenScreenOff) {
649 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500650 }
Julia Reynoldsd5607292016-02-05 15:25:58 -0500651 if (!allowWhenScreenOn) {
Julia Reynolds61721582016-01-05 08:35:25 -0500652 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_ON;
653 }
John Spurlocka492d1d2015-05-05 18:30:28 -0400654 priorityCallSenders = sourceToPrioritySenders(allowCallsFrom, priorityCallSenders);
655 priorityMessageSenders = sourceToPrioritySenders(allowMessagesFrom, priorityMessageSenders);
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500656 return new Policy(priorityCategories, priorityCallSenders, priorityMessageSenders,
657 suppressedVisualEffects);
John Spurlocka492d1d2015-05-05 18:30:28 -0400658 }
659
660 private static int sourceToPrioritySenders(int source, int def) {
661 switch (source) {
662 case SOURCE_ANYONE: return Policy.PRIORITY_SENDERS_ANY;
663 case SOURCE_CONTACT: return Policy.PRIORITY_SENDERS_CONTACTS;
664 case SOURCE_STAR: return Policy.PRIORITY_SENDERS_STARRED;
665 default: return def;
John Spurlock1fc476d2015-04-14 16:05:20 -0400666 }
John Spurlocka492d1d2015-05-05 18:30:28 -0400667 }
668
669 private static int prioritySendersToSource(int prioritySenders, int def) {
670 switch (prioritySenders) {
671 case Policy.PRIORITY_SENDERS_CONTACTS: return SOURCE_CONTACT;
672 case Policy.PRIORITY_SENDERS_STARRED: return SOURCE_STAR;
673 case Policy.PRIORITY_SENDERS_ANY: return SOURCE_ANYONE;
674 default: return def;
675 }
John Spurlock1fc476d2015-04-14 16:05:20 -0400676 }
677
678 public void applyNotificationPolicy(Policy policy) {
679 if (policy == null) return;
680 allowCalls = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_CALLS) != 0;
681 allowMessages = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MESSAGES) != 0;
682 allowEvents = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_EVENTS) != 0;
683 allowReminders = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REMINDERS) != 0;
684 allowRepeatCallers = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REPEAT_CALLERS)
685 != 0;
John Spurlock80774932015-05-07 17:38:50 -0400686 allowCallsFrom = prioritySendersToSource(policy.priorityCallSenders, allowCallsFrom);
687 allowMessagesFrom = prioritySendersToSource(policy.priorityMessageSenders,
688 allowMessagesFrom);
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500689 if (policy.suppressedVisualEffects != Policy.SUPPRESSED_EFFECTS_UNSET) {
Julia Reynoldsd5607292016-02-05 15:25:58 -0500690 allowWhenScreenOff =
691 (policy.suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_SCREEN_OFF) == 0;
692 allowWhenScreenOn =
Julia Reynolds61721582016-01-05 08:35:25 -0500693 (policy.suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_SCREEN_ON) == 0;
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500694 }
John Spurlock1fc476d2015-04-14 16:05:20 -0400695 }
696
Selim Cinek9c4a7072014-11-21 17:44:34 +0100697 public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle) {
John Spurlockbbde2672015-05-13 15:42:04 -0400698 return toTimeCondition(context, minutesFromNow, userHandle, false /*shortVersion*/);
699 }
700
701 public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle,
702 boolean shortVersion) {
Jason Monk4dd81462014-09-08 10:13:59 -0400703 final long now = System.currentTimeMillis();
704 final long millis = minutesFromNow == 0 ? ZERO_VALUE_MS : minutesFromNow * MINUTES_MS;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400705 return toTimeCondition(context, now + millis, minutesFromNow, userHandle, shortVersion);
Jason Monk4dd81462014-09-08 10:13:59 -0400706 }
707
Julia Reynolds0842fe82015-10-01 14:34:22 -0400708 public static Condition toTimeCondition(Context context, long time, int minutes,
John Spurlockbbde2672015-05-13 15:42:04 -0400709 int userHandle, boolean shortVersion) {
Julia Reynolds0842fe82015-10-01 14:34:22 -0400710 final int num;
711 String summary, line1, line2;
Julia Reynolds8e2d2bb2015-10-01 14:34:22 -0400712 final CharSequence formattedTime = getFormattedTime(context, time, userHandle);
Julia Reynolds0842fe82015-10-01 14:34:22 -0400713 final Resources res = context.getResources();
John Spurlockc90e6fe2014-10-28 11:21:42 -0400714 if (minutes < 60) {
715 // display as minutes
716 num = minutes;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400717 int summaryResId = shortVersion ? R.plurals.zen_mode_duration_minutes_summary_short
John Spurlockbbde2672015-05-13 15:42:04 -0400718 : R.plurals.zen_mode_duration_minutes_summary;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400719 summary = res.getQuantityString(summaryResId, num, num, formattedTime);
720 int line1ResId = shortVersion ? R.plurals.zen_mode_duration_minutes_short
John Spurlockbbde2672015-05-13 15:42:04 -0400721 : R.plurals.zen_mode_duration_minutes;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400722 line1 = res.getQuantityString(line1ResId, num, num, formattedTime);
723 line2 = res.getString(R.string.zen_mode_until, formattedTime);
724 } else if (minutes < DAY_MINUTES) {
John Spurlockc90e6fe2014-10-28 11:21:42 -0400725 // display as hours
726 num = Math.round(minutes / 60f);
Julia Reynolds0842fe82015-10-01 14:34:22 -0400727 int summaryResId = shortVersion ? R.plurals.zen_mode_duration_hours_summary_short
John Spurlockbbde2672015-05-13 15:42:04 -0400728 : R.plurals.zen_mode_duration_hours_summary;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400729 summary = res.getQuantityString(summaryResId, num, num, formattedTime);
730 int line1ResId = shortVersion ? R.plurals.zen_mode_duration_hours_short
John Spurlockbbde2672015-05-13 15:42:04 -0400731 : R.plurals.zen_mode_duration_hours;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400732 line1 = res.getQuantityString(line1ResId, num, num, formattedTime);
733 line2 = res.getString(R.string.zen_mode_until, formattedTime);
734 } else {
735 // display as day/time
736 summary = line1 = line2 = res.getString(R.string.zen_mode_until, formattedTime);
John Spurlockc90e6fe2014-10-28 11:21:42 -0400737 }
Jason Monk4dd81462014-09-08 10:13:59 -0400738 final Uri id = toCountdownConditionId(time);
John Spurlockc90e6fe2014-10-28 11:21:42 -0400739 return new Condition(id, summary, line1, line2, 0, Condition.STATE_TRUE,
Jason Monk4dd81462014-09-08 10:13:59 -0400740 Condition.FLAG_RELEVANT_NOW);
741 }
742
Julia Reynolds0842fe82015-10-01 14:34:22 -0400743 public static Condition toNextAlarmCondition(Context context, long now, long alarm,
744 int userHandle) {
Julia Reynolds8e2d2bb2015-10-01 14:34:22 -0400745 final CharSequence formattedTime = getFormattedTime(context, alarm, userHandle);
Julia Reynolds0842fe82015-10-01 14:34:22 -0400746 final Resources res = context.getResources();
747 final String line1 = res.getString(R.string.zen_mode_alarm, formattedTime);
748 final Uri id = toCountdownConditionId(alarm);
749 return new Condition(id, "", line1, "", 0, Condition.STATE_TRUE,
750 Condition.FLAG_RELEVANT_NOW);
751 }
752
Julia Reynolds8e2d2bb2015-10-01 14:34:22 -0400753 private static CharSequence getFormattedTime(Context context, long time, int userHandle) {
754 String skeleton = "EEE " + (DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma");
755 GregorianCalendar now = new GregorianCalendar();
756 GregorianCalendar endTime = new GregorianCalendar();
757 endTime.setTimeInMillis(time);
758 if (now.get(Calendar.YEAR) == endTime.get(Calendar.YEAR)
759 && now.get(Calendar.MONTH) == endTime.get(Calendar.MONTH)
760 && now.get(Calendar.DATE) == endTime.get(Calendar.DATE)) {
761 skeleton = DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma";
Julia Reynolds0842fe82015-10-01 14:34:22 -0400762 }
763 final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
764 return DateFormat.format(pattern, time);
765 }
766
John Spurlockd60258f2015-04-30 09:30:52 -0400767 // ==== Built-in system conditions ====
768
John Spurlock37bc92c2014-11-03 11:01:51 -0500769 public static final String SYSTEM_AUTHORITY = "android";
John Spurlock4db0d982014-08-13 09:19:03 -0400770
John Spurlockd60258f2015-04-30 09:30:52 -0400771 // ==== Built-in system condition: countdown ====
772
John Spurlock530052a2014-11-30 16:26:19 -0500773 public static final String COUNTDOWN_PATH = "countdown";
John Spurlock856edeb2014-06-01 20:36:47 -0400774
775 public static Uri toCountdownConditionId(long time) {
776 return new Uri.Builder().scheme(Condition.SCHEME)
John Spurlock4db0d982014-08-13 09:19:03 -0400777 .authority(SYSTEM_AUTHORITY)
John Spurlock856edeb2014-06-01 20:36:47 -0400778 .appendPath(COUNTDOWN_PATH)
779 .appendPath(Long.toString(time))
780 .build();
781 }
782
783 public static long tryParseCountdownConditionId(Uri conditionId) {
John Spurlock4db0d982014-08-13 09:19:03 -0400784 if (!Condition.isValidId(conditionId, SYSTEM_AUTHORITY)) return 0;
John Spurlock856edeb2014-06-01 20:36:47 -0400785 if (conditionId.getPathSegments().size() != 2
786 || !COUNTDOWN_PATH.equals(conditionId.getPathSegments().get(0))) return 0;
787 try {
788 return Long.parseLong(conditionId.getPathSegments().get(1));
789 } catch (RuntimeException e) {
790 Slog.w(TAG, "Error parsing countdown condition: " + conditionId, e);
791 return 0;
792 }
793 }
794
795 public static boolean isValidCountdownConditionId(Uri conditionId) {
796 return tryParseCountdownConditionId(conditionId) != 0;
797 }
John Spurlock4db0d982014-08-13 09:19:03 -0400798
John Spurlockd60258f2015-04-30 09:30:52 -0400799 // ==== Built-in system condition: schedule ====
800
John Spurlockb2278d62015-04-07 12:47:12 -0400801 public static final String SCHEDULE_PATH = "schedule";
John Spurlock4db0d982014-08-13 09:19:03 -0400802
John Spurlockd60258f2015-04-30 09:30:52 -0400803 public static Uri toScheduleConditionId(ScheduleInfo schedule) {
804 return new Uri.Builder().scheme(Condition.SCHEME)
805 .authority(SYSTEM_AUTHORITY)
806 .appendPath(SCHEDULE_PATH)
807 .appendQueryParameter("days", toDayList(schedule.days))
808 .appendQueryParameter("start", schedule.startHour + "." + schedule.startMinute)
809 .appendQueryParameter("end", schedule.endHour + "." + schedule.endMinute)
Julia Reynolds1998ee52016-02-11 13:49:08 -0500810 .appendQueryParameter("exitAtAlarm", String.valueOf(schedule.exitAtAlarm))
John Spurlockd60258f2015-04-30 09:30:52 -0400811 .build();
812 }
813
814 public static boolean isValidScheduleConditionId(Uri conditionId) {
815 return tryParseScheduleConditionId(conditionId) != null;
816 }
817
818 public static ScheduleInfo tryParseScheduleConditionId(Uri conditionId) {
819 final boolean isSchedule = conditionId != null
820 && conditionId.getScheme().equals(Condition.SCHEME)
821 && conditionId.getAuthority().equals(ZenModeConfig.SYSTEM_AUTHORITY)
822 && conditionId.getPathSegments().size() == 1
823 && conditionId.getPathSegments().get(0).equals(ZenModeConfig.SCHEDULE_PATH);
824 if (!isSchedule) return null;
825 final int[] start = tryParseHourAndMinute(conditionId.getQueryParameter("start"));
826 final int[] end = tryParseHourAndMinute(conditionId.getQueryParameter("end"));
827 if (start == null || end == null) return null;
828 final ScheduleInfo rt = new ScheduleInfo();
829 rt.days = tryParseDayList(conditionId.getQueryParameter("days"), "\\.");
830 rt.startHour = start[0];
831 rt.startMinute = start[1];
832 rt.endHour = end[0];
833 rt.endMinute = end[1];
Julia Reynolds1998ee52016-02-11 13:49:08 -0500834 rt.exitAtAlarm = safeBoolean(conditionId.getQueryParameter("exitAtAlarm"), false);
John Spurlockd60258f2015-04-30 09:30:52 -0400835 return rt;
836 }
837
Julia Reynoldsa47a27f2015-08-24 08:31:47 -0400838 public static ComponentName getScheduleConditionProvider() {
839 return new ComponentName(SYSTEM_AUTHORITY, "ScheduleConditionProvider");
840 }
841
John Spurlockb2278d62015-04-07 12:47:12 -0400842 public static class ScheduleInfo {
843 public int[] days;
844 public int startHour;
845 public int startMinute;
846 public int endHour;
847 public int endMinute;
Julia Reynolds1998ee52016-02-11 13:49:08 -0500848 public boolean exitAtAlarm;
849 public long nextAlarm;
John Spurlockb2278d62015-04-07 12:47:12 -0400850
851 @Override
852 public int hashCode() {
853 return 0;
854 }
855
856 @Override
857 public boolean equals(Object o) {
858 if (!(o instanceof ScheduleInfo)) return false;
859 final ScheduleInfo other = (ScheduleInfo) o;
860 return toDayList(days).equals(toDayList(other.days))
861 && startHour == other.startHour
862 && startMinute == other.startMinute
863 && endHour == other.endHour
Julia Reynolds1998ee52016-02-11 13:49:08 -0500864 && endMinute == other.endMinute
865 && exitAtAlarm == other.exitAtAlarm;
John Spurlockb2278d62015-04-07 12:47:12 -0400866 }
867
868 public ScheduleInfo copy() {
869 final ScheduleInfo rt = new ScheduleInfo();
870 if (days != null) {
871 rt.days = new int[days.length];
872 System.arraycopy(days, 0, rt.days, 0, days.length);
873 }
874 rt.startHour = startHour;
875 rt.startMinute = startMinute;
876 rt.endHour = endHour;
877 rt.endMinute = endMinute;
Julia Reynolds1998ee52016-02-11 13:49:08 -0500878 rt.exitAtAlarm = exitAtAlarm;
879 rt.nextAlarm = nextAlarm;
John Spurlockb2278d62015-04-07 12:47:12 -0400880 return rt;
881 }
Julia Reynolds7f733082016-04-26 14:19:19 -0400882
883 @Override
884 public String toString() {
885 return "ScheduleInfo{" +
886 "days=" + Arrays.toString(days) +
887 ", startHour=" + startHour +
888 ", startMinute=" + startMinute +
889 ", endHour=" + endHour +
890 ", endMinute=" + endMinute +
891 ", exitAtAlarm=" + exitAtAlarm +
892 ", nextAlarm=" + nextAlarm +
893 '}';
894 }
John Spurlockb2278d62015-04-07 12:47:12 -0400895 }
896
John Spurlockd60258f2015-04-30 09:30:52 -0400897 // ==== Built-in system condition: event ====
898
899 public static final String EVENT_PATH = "event";
900
901 public static Uri toEventConditionId(EventInfo event) {
John Spurlock4db0d982014-08-13 09:19:03 -0400902 return new Uri.Builder().scheme(Condition.SCHEME)
903 .authority(SYSTEM_AUTHORITY)
John Spurlockd60258f2015-04-30 09:30:52 -0400904 .appendPath(EVENT_PATH)
John Spurlock1b8b22b2015-05-20 09:47:13 -0400905 .appendQueryParameter("userId", Long.toString(event.userId))
John Spurlock995a7492015-05-28 22:13:03 -0400906 .appendQueryParameter("calendar", event.calendar != null ? event.calendar : "")
John Spurlockd60258f2015-04-30 09:30:52 -0400907 .appendQueryParameter("reply", Integer.toString(event.reply))
John Spurlock4db0d982014-08-13 09:19:03 -0400908 .build();
909 }
910
John Spurlockd60258f2015-04-30 09:30:52 -0400911 public static boolean isValidEventConditionId(Uri conditionId) {
912 return tryParseEventConditionId(conditionId) != null;
John Spurlockb2278d62015-04-07 12:47:12 -0400913 }
914
John Spurlockd60258f2015-04-30 09:30:52 -0400915 public static EventInfo tryParseEventConditionId(Uri conditionId) {
916 final boolean isEvent = conditionId != null
John Spurlockb2278d62015-04-07 12:47:12 -0400917 && conditionId.getScheme().equals(Condition.SCHEME)
918 && conditionId.getAuthority().equals(ZenModeConfig.SYSTEM_AUTHORITY)
919 && conditionId.getPathSegments().size() == 1
John Spurlockd60258f2015-04-30 09:30:52 -0400920 && conditionId.getPathSegments().get(0).equals(EVENT_PATH);
921 if (!isEvent) return null;
922 final EventInfo rt = new EventInfo();
John Spurlock1b8b22b2015-05-20 09:47:13 -0400923 rt.userId = tryParseInt(conditionId.getQueryParameter("userId"), UserHandle.USER_NULL);
John Spurlock995a7492015-05-28 22:13:03 -0400924 rt.calendar = conditionId.getQueryParameter("calendar");
925 if (TextUtils.isEmpty(rt.calendar) || tryParseLong(rt.calendar, -1L) != -1L) {
926 rt.calendar = null;
927 }
John Spurlockd60258f2015-04-30 09:30:52 -0400928 rt.reply = tryParseInt(conditionId.getQueryParameter("reply"), 0);
John Spurlockb2278d62015-04-07 12:47:12 -0400929 return rt;
John Spurlock4db0d982014-08-13 09:19:03 -0400930 }
931
Julia Reynoldsa47a27f2015-08-24 08:31:47 -0400932 public static ComponentName getEventConditionProvider() {
933 return new ComponentName(SYSTEM_AUTHORITY, "EventConditionProvider");
934 }
935
John Spurlockd60258f2015-04-30 09:30:52 -0400936 public static class EventInfo {
John Spurlockd39af2d2015-05-05 09:49:32 -0400937 public static final int REPLY_ANY_EXCEPT_NO = 0;
938 public static final int REPLY_YES_OR_MAYBE = 1;
John Spurlockd60258f2015-04-30 09:30:52 -0400939 public static final int REPLY_YES = 2;
940
John Spurlock1b8b22b2015-05-20 09:47:13 -0400941 public int userId = UserHandle.USER_NULL; // USER_NULL = unspecified - use current user
John Spurlock995a7492015-05-28 22:13:03 -0400942 public String calendar; // CalendarContract.Calendars.OWNER_ACCOUNT, or null for any
John Spurlockd60258f2015-04-30 09:30:52 -0400943 public int reply;
944
945 @Override
946 public int hashCode() {
947 return 0;
948 }
949
950 @Override
951 public boolean equals(Object o) {
952 if (!(o instanceof EventInfo)) return false;
953 final EventInfo other = (EventInfo) o;
John Spurlock1b8b22b2015-05-20 09:47:13 -0400954 return userId == other.userId
John Spurlock995a7492015-05-28 22:13:03 -0400955 && Objects.equals(calendar, other.calendar)
John Spurlockd60258f2015-04-30 09:30:52 -0400956 && reply == other.reply;
957 }
958
959 public EventInfo copy() {
960 final EventInfo rt = new EventInfo();
John Spurlock1b8b22b2015-05-20 09:47:13 -0400961 rt.userId = userId;
John Spurlockd60258f2015-04-30 09:30:52 -0400962 rt.calendar = calendar;
John Spurlockd60258f2015-04-30 09:30:52 -0400963 rt.reply = reply;
964 return rt;
965 }
John Spurlock1b8b22b2015-05-20 09:47:13 -0400966
967 public static int resolveUserId(int userId) {
968 return userId == UserHandle.USER_NULL ? ActivityManager.getCurrentUser() : userId;
John Spurlock1b8b22b2015-05-20 09:47:13 -0400969 }
John Spurlockd60258f2015-04-30 09:30:52 -0400970 }
971
972 // ==== End built-in system conditions ====
973
John Spurlock4db0d982014-08-13 09:19:03 -0400974 private static int[] tryParseHourAndMinute(String value) {
975 if (TextUtils.isEmpty(value)) return null;
976 final int i = value.indexOf('.');
977 if (i < 1 || i >= value.length() - 1) return null;
978 final int hour = tryParseInt(value.substring(0, i), -1);
979 final int minute = tryParseInt(value.substring(i + 1), -1);
980 return isValidHour(hour) && isValidMinute(minute) ? new int[] { hour, minute } : null;
981 }
982
John Spurlockb2278d62015-04-07 12:47:12 -0400983 private static int tryParseZenMode(String value, int defValue) {
984 final int rt = tryParseInt(value, defValue);
985 return Global.isValidZenMode(rt) ? rt : defValue;
John Spurlock4db0d982014-08-13 09:19:03 -0400986 }
987
Julia Reynolds4fe98d62015-10-06 16:23:41 -0400988 public static String newRuleId() {
John Spurlockb2278d62015-04-07 12:47:12 -0400989 return UUID.randomUUID().toString().replace("-", "");
990 }
991
John Spurlockb2278d62015-04-07 12:47:12 -0400992 public static String getConditionSummary(Context context, ZenModeConfig config,
John Spurlockbbde2672015-05-13 15:42:04 -0400993 int userHandle, boolean shortVersion) {
994 return getConditionLine(context, config, userHandle, false /*useLine1*/, shortVersion);
John Spurlockb2278d62015-04-07 12:47:12 -0400995 }
996
997 private static String getConditionLine(Context context, ZenModeConfig config,
John Spurlockbbde2672015-05-13 15:42:04 -0400998 int userHandle, boolean useLine1, boolean shortVersion) {
John Spurlockb2278d62015-04-07 12:47:12 -0400999 if (config == null) return "";
1000 if (config.manualRule != null) {
1001 final Uri id = config.manualRule.conditionId;
1002 if (id == null) {
1003 return context.getString(com.android.internal.R.string.zen_mode_forever);
1004 }
1005 final long time = tryParseCountdownConditionId(id);
1006 Condition c = config.manualRule.condition;
1007 if (time > 0) {
1008 final long now = System.currentTimeMillis();
1009 final long span = time - now;
Julia Reynolds0842fe82015-10-01 14:34:22 -04001010 c = toTimeCondition(context, time, Math.round(span / (float) MINUTES_MS),
1011 userHandle, shortVersion);
John Spurlockb2278d62015-04-07 12:47:12 -04001012 }
1013 final String rt = c == null ? "" : useLine1 ? c.line1 : c.summary;
1014 return TextUtils.isEmpty(rt) ? "" : rt;
1015 }
1016 String summary = "";
1017 for (ZenRule automaticRule : config.automaticRules.values()) {
John Spurlock39581cc2015-04-10 11:59:01 -04001018 if (automaticRule.isAutomaticActive()) {
John Spurlockb2278d62015-04-07 12:47:12 -04001019 if (summary.isEmpty()) {
1020 summary = automaticRule.name;
1021 } else {
1022 summary = context.getResources()
1023 .getString(R.string.zen_mode_rule_name_combination, summary,
1024 automaticRule.name);
1025 }
1026 }
1027 }
1028 return summary;
1029 }
1030
1031 public static class ZenRule implements Parcelable {
1032 public boolean enabled;
1033 public boolean snoozing; // user manually disabled this instance
Julia Reynolds361e82d32016-02-26 18:19:49 -05001034 public String name; // required for automatic
John Spurlockb2278d62015-04-07 12:47:12 -04001035 public int zenMode;
1036 public Uri conditionId; // required for automatic
1037 public Condition condition; // optional
1038 public ComponentName component; // optional
Julia Reynolds56106ff2015-09-30 14:42:53 -04001039 public String id; // required for automatic (unique)
1040 public long creationTime; // required for automatic
John Spurlockb2278d62015-04-07 12:47:12 -04001041
1042 public ZenRule() { }
1043
1044 public ZenRule(Parcel source) {
1045 enabled = source.readInt() == 1;
1046 snoozing = source.readInt() == 1;
1047 if (source.readInt() == 1) {
1048 name = source.readString();
1049 }
1050 zenMode = source.readInt();
1051 conditionId = source.readParcelable(null);
1052 condition = source.readParcelable(null);
1053 component = source.readParcelable(null);
Julia Reynolds56106ff2015-09-30 14:42:53 -04001054 if (source.readInt() == 1) {
1055 id = source.readString();
1056 }
1057 creationTime = source.readLong();
John Spurlockb2278d62015-04-07 12:47:12 -04001058 }
John Spurlock4db0d982014-08-13 09:19:03 -04001059
1060 @Override
John Spurlockb2278d62015-04-07 12:47:12 -04001061 public int describeContents() {
John Spurlock4db0d982014-08-13 09:19:03 -04001062 return 0;
1063 }
1064
1065 @Override
John Spurlockb2278d62015-04-07 12:47:12 -04001066 public void writeToParcel(Parcel dest, int flags) {
1067 dest.writeInt(enabled ? 1 : 0);
1068 dest.writeInt(snoozing ? 1 : 0);
1069 if (name != null) {
1070 dest.writeInt(1);
1071 dest.writeString(name);
1072 } else {
1073 dest.writeInt(0);
1074 }
1075 dest.writeInt(zenMode);
1076 dest.writeParcelable(conditionId, 0);
1077 dest.writeParcelable(condition, 0);
1078 dest.writeParcelable(component, 0);
Julia Reynolds56106ff2015-09-30 14:42:53 -04001079 if (id != null) {
1080 dest.writeInt(1);
1081 dest.writeString(id);
1082 } else {
1083 dest.writeInt(0);
1084 }
1085 dest.writeLong(creationTime);
John Spurlockb2278d62015-04-07 12:47:12 -04001086 }
1087
1088 @Override
1089 public String toString() {
1090 return new StringBuilder(ZenRule.class.getSimpleName()).append('[')
1091 .append("enabled=").append(enabled)
1092 .append(",snoozing=").append(snoozing)
1093 .append(",name=").append(name)
1094 .append(",zenMode=").append(Global.zenModeToString(zenMode))
1095 .append(",conditionId=").append(conditionId)
1096 .append(",condition=").append(condition)
1097 .append(",component=").append(component)
Julia Reynolds56106ff2015-09-30 14:42:53 -04001098 .append(",id=").append(id)
1099 .append(",creationTime=").append(creationTime)
John Spurlockb2278d62015-04-07 12:47:12 -04001100 .append(']').toString();
1101 }
1102
John Spurlock21258a32015-05-27 18:22:55 -04001103 private static void appendDiff(Diff d, String item, ZenRule from, ZenRule to) {
1104 if (d == null) return;
1105 if (from == null) {
1106 if (to != null) {
1107 d.addLine(item, "insert");
1108 }
1109 return;
1110 }
1111 from.appendDiff(d, item, to);
1112 }
1113
1114 private void appendDiff(Diff d, String item, ZenRule to) {
1115 if (to == null) {
1116 d.addLine(item, "delete");
1117 return;
1118 }
1119 if (enabled != to.enabled) {
1120 d.addLine(item, "enabled", enabled, to.enabled);
1121 }
1122 if (snoozing != to.snoozing) {
1123 d.addLine(item, "snoozing", snoozing, to.snoozing);
1124 }
1125 if (!Objects.equals(name, to.name)) {
1126 d.addLine(item, "name", name, to.name);
1127 }
1128 if (zenMode != to.zenMode) {
1129 d.addLine(item, "zenMode", zenMode, to.zenMode);
1130 }
1131 if (!Objects.equals(conditionId, to.conditionId)) {
1132 d.addLine(item, "conditionId", conditionId, to.conditionId);
1133 }
1134 if (!Objects.equals(condition, to.condition)) {
1135 d.addLine(item, "condition", condition, to.condition);
1136 }
1137 if (!Objects.equals(component, to.component)) {
1138 d.addLine(item, "component", component, to.component);
1139 }
Julia Reynolds56106ff2015-09-30 14:42:53 -04001140 if (!Objects.equals(id, to.id)) {
1141 d.addLine(item, "id", id, to.id);
1142 }
Julia Reynoldsc8e54e82015-11-30 16:43:05 -05001143 if (creationTime != to.creationTime) {
Julia Reynolds56106ff2015-09-30 14:42:53 -04001144 d.addLine(item, "creationTime", creationTime, to.creationTime);
1145 }
John Spurlock21258a32015-05-27 18:22:55 -04001146 }
1147
John Spurlockb2278d62015-04-07 12:47:12 -04001148 @Override
John Spurlock4db0d982014-08-13 09:19:03 -04001149 public boolean equals(Object o) {
John Spurlockb2278d62015-04-07 12:47:12 -04001150 if (!(o instanceof ZenRule)) return false;
1151 if (o == this) return true;
1152 final ZenRule other = (ZenRule) o;
1153 return other.enabled == enabled
1154 && other.snoozing == snoozing
1155 && Objects.equals(other.name, name)
1156 && other.zenMode == zenMode
1157 && Objects.equals(other.conditionId, conditionId)
1158 && Objects.equals(other.condition, condition)
Julia Reynolds56106ff2015-09-30 14:42:53 -04001159 && Objects.equals(other.component, component)
1160 && Objects.equals(other.id, id)
1161 && other.creationTime == creationTime;
John Spurlockb2278d62015-04-07 12:47:12 -04001162 }
1163
1164 @Override
1165 public int hashCode() {
1166 return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
Julia Reynolds56106ff2015-09-30 14:42:53 -04001167 component, id, creationTime);
John Spurlockb2278d62015-04-07 12:47:12 -04001168 }
1169
John Spurlock39581cc2015-04-10 11:59:01 -04001170 public boolean isAutomaticActive() {
1171 return enabled && !snoozing && component != null && isTrueOrUnknown();
1172 }
1173
John Spurlockb2278d62015-04-07 12:47:12 -04001174 public boolean isTrueOrUnknown() {
John Spurlock39581cc2015-04-10 11:59:01 -04001175 return condition != null && (condition.state == Condition.STATE_TRUE
1176 || condition.state == Condition.STATE_UNKNOWN);
John Spurlockb2278d62015-04-07 12:47:12 -04001177 }
1178
1179 public static final Parcelable.Creator<ZenRule> CREATOR
1180 = new Parcelable.Creator<ZenRule>() {
1181 @Override
1182 public ZenRule createFromParcel(Parcel source) {
1183 return new ZenRule(source);
1184 }
1185 @Override
1186 public ZenRule[] newArray(int size) {
1187 return new ZenRule[size];
1188 }
1189 };
1190 }
1191
1192 // Legacy config
1193 public static final class XmlV1 {
1194 public static final String SLEEP_MODE_NIGHTS = "nights";
1195 public static final String SLEEP_MODE_WEEKNIGHTS = "weeknights";
1196 public static final String SLEEP_MODE_DAYS_PREFIX = "days:";
1197
1198 private static final String EXIT_CONDITION_TAG = "exitCondition";
1199 private static final String EXIT_CONDITION_ATT_COMPONENT = "component";
1200 private static final String SLEEP_TAG = "sleep";
1201 private static final String SLEEP_ATT_MODE = "mode";
1202 private static final String SLEEP_ATT_NONE = "none";
1203
1204 private static final String SLEEP_ATT_START_HR = "startHour";
1205 private static final String SLEEP_ATT_START_MIN = "startMin";
1206 private static final String SLEEP_ATT_END_HR = "endHour";
1207 private static final String SLEEP_ATT_END_MIN = "endMin";
1208
1209 public boolean allowCalls;
1210 public boolean allowMessages;
1211 public boolean allowReminders = DEFAULT_ALLOW_REMINDERS;
1212 public boolean allowEvents = DEFAULT_ALLOW_EVENTS;
1213 public int allowFrom = SOURCE_ANYONE;
1214
1215 public String sleepMode; // nights, weeknights, days:1,2,3 Calendar.days
1216 public int sleepStartHour; // 0-23
1217 public int sleepStartMinute; // 0-59
1218 public int sleepEndHour;
1219 public int sleepEndMinute;
1220 public boolean sleepNone; // false = priority, true = none
1221 public ComponentName[] conditionComponents;
1222 public Uri[] conditionIds;
1223 public Condition exitCondition; // manual exit condition
1224 public ComponentName exitConditionComponent; // manual exit condition component
1225
1226 private static boolean isValidSleepMode(String sleepMode) {
1227 return sleepMode == null || sleepMode.equals(SLEEP_MODE_NIGHTS)
1228 || sleepMode.equals(SLEEP_MODE_WEEKNIGHTS) || tryParseDays(sleepMode) != null;
1229 }
1230
1231 public static int[] tryParseDays(String sleepMode) {
1232 if (sleepMode == null) return null;
1233 sleepMode = sleepMode.trim();
1234 if (SLEEP_MODE_NIGHTS.equals(sleepMode)) return ALL_DAYS;
1235 if (SLEEP_MODE_WEEKNIGHTS.equals(sleepMode)) return WEEKNIGHT_DAYS;
1236 if (!sleepMode.startsWith(SLEEP_MODE_DAYS_PREFIX)) return null;
1237 if (sleepMode.equals(SLEEP_MODE_DAYS_PREFIX)) return null;
1238 return tryParseDayList(sleepMode.substring(SLEEP_MODE_DAYS_PREFIX.length()), ",");
1239 }
1240
1241 public static XmlV1 readXml(XmlPullParser parser)
1242 throws XmlPullParserException, IOException {
1243 int type;
1244 String tag;
1245 XmlV1 rt = new XmlV1();
1246 final ArrayList<ComponentName> conditionComponents = new ArrayList<ComponentName>();
1247 final ArrayList<Uri> conditionIds = new ArrayList<Uri>();
1248 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
1249 tag = parser.getName();
1250 if (type == XmlPullParser.END_TAG && ZEN_TAG.equals(tag)) {
1251 if (!conditionComponents.isEmpty()) {
1252 rt.conditionComponents = conditionComponents
1253 .toArray(new ComponentName[conditionComponents.size()]);
1254 rt.conditionIds = conditionIds.toArray(new Uri[conditionIds.size()]);
1255 }
1256 return rt;
1257 }
1258 if (type == XmlPullParser.START_TAG) {
1259 if (ALLOW_TAG.equals(tag)) {
1260 rt.allowCalls = safeBoolean(parser, ALLOW_ATT_CALLS, false);
1261 rt.allowMessages = safeBoolean(parser, ALLOW_ATT_MESSAGES, false);
1262 rt.allowReminders = safeBoolean(parser, ALLOW_ATT_REMINDERS,
1263 DEFAULT_ALLOW_REMINDERS);
1264 rt.allowEvents = safeBoolean(parser, ALLOW_ATT_EVENTS,
1265 DEFAULT_ALLOW_EVENTS);
1266 rt.allowFrom = safeInt(parser, ALLOW_ATT_FROM, SOURCE_ANYONE);
1267 if (rt.allowFrom < SOURCE_ANYONE || rt.allowFrom > MAX_SOURCE) {
1268 throw new IndexOutOfBoundsException("bad source in config:"
1269 + rt.allowFrom);
1270 }
1271 } else if (SLEEP_TAG.equals(tag)) {
1272 final String mode = parser.getAttributeValue(null, SLEEP_ATT_MODE);
1273 rt.sleepMode = isValidSleepMode(mode)? mode : null;
1274 rt.sleepNone = safeBoolean(parser, SLEEP_ATT_NONE, false);
1275 final int startHour = safeInt(parser, SLEEP_ATT_START_HR, 0);
1276 final int startMinute = safeInt(parser, SLEEP_ATT_START_MIN, 0);
1277 final int endHour = safeInt(parser, SLEEP_ATT_END_HR, 0);
1278 final int endMinute = safeInt(parser, SLEEP_ATT_END_MIN, 0);
1279 rt.sleepStartHour = isValidHour(startHour) ? startHour : 0;
1280 rt.sleepStartMinute = isValidMinute(startMinute) ? startMinute : 0;
1281 rt.sleepEndHour = isValidHour(endHour) ? endHour : 0;
1282 rt.sleepEndMinute = isValidMinute(endMinute) ? endMinute : 0;
1283 } else if (CONDITION_TAG.equals(tag)) {
1284 final ComponentName component =
1285 safeComponentName(parser, CONDITION_ATT_COMPONENT);
1286 final Uri conditionId = safeUri(parser, CONDITION_ATT_ID);
1287 if (component != null && conditionId != null) {
1288 conditionComponents.add(component);
1289 conditionIds.add(conditionId);
1290 }
1291 } else if (EXIT_CONDITION_TAG.equals(tag)) {
1292 rt.exitCondition = readConditionXml(parser);
1293 if (rt.exitCondition != null) {
1294 rt.exitConditionComponent =
1295 safeComponentName(parser, EXIT_CONDITION_ATT_COMPONENT);
1296 }
1297 }
1298 }
1299 }
1300 throw new IllegalStateException("Failed to reach END_DOCUMENT");
John Spurlock4db0d982014-08-13 09:19:03 -04001301 }
1302 }
John Spurlock530052a2014-11-30 16:26:19 -05001303
John Spurlockb2278d62015-04-07 12:47:12 -04001304 public interface Migration {
1305 ZenModeConfig migrate(XmlV1 v1);
1306 }
John Spurlock1fc476d2015-04-14 16:05:20 -04001307
John Spurlock21258a32015-05-27 18:22:55 -04001308 public static class Diff {
1309 private final ArrayList<String> lines = new ArrayList<>();
1310
1311 @Override
1312 public String toString() {
1313 final StringBuilder sb = new StringBuilder("Diff[");
1314 final int N = lines.size();
1315 for (int i = 0; i < N; i++) {
1316 if (i > 0) {
1317 sb.append(',');
1318 }
1319 sb.append(lines.get(i));
1320 }
1321 return sb.append(']').toString();
1322 }
1323
1324 private Diff addLine(String item, String action) {
1325 lines.add(item + ":" + action);
1326 return this;
1327 }
1328
1329 public Diff addLine(String item, String subitem, Object from, Object to) {
1330 return addLine(item + "." + subitem, from, to);
1331 }
1332
1333 public Diff addLine(String item, Object from, Object to) {
1334 return addLine(item, from + "->" + to);
1335 }
1336 }
1337
John Spurlock056c5192014-04-20 21:52:01 -04001338}