blob: 97939a9ba3afa119b775c6ee5e19fb349dfbae58 [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;
John Spurlockae641c92014-06-30 18:11:40 -040043import java.util.Calendar;
Julia Reynolds8e2d2bb2015-10-01 14:34:22 -040044import java.util.GregorianCalendar;
John Spurlockc90e6fe2014-10-28 11:21:42 -040045import java.util.Locale;
John Spurlock056c5192014-04-20 21:52:01 -040046import java.util.Objects;
John Spurlockb2278d62015-04-07 12:47:12 -040047import java.util.UUID;
John Spurlockc90e6fe2014-10-28 11:21:42 -040048
John Spurlock056c5192014-04-20 21:52:01 -040049/**
50 * Persisted configuration for zen mode.
51 *
52 * @hide
53 */
54public class ZenModeConfig implements Parcelable {
John Spurlock856edeb2014-06-01 20:36:47 -040055 private static String TAG = "ZenModeConfig";
John Spurlock056c5192014-04-20 21:52:01 -040056
Chris Wren99f963e2014-05-28 16:52:42 -040057 public static final int SOURCE_ANYONE = 0;
58 public static final int SOURCE_CONTACT = 1;
59 public static final int SOURCE_STAR = 2;
60 public static final int MAX_SOURCE = SOURCE_STAR;
John Spurlocka492d1d2015-05-05 18:30:28 -040061 private static final int DEFAULT_SOURCE = SOURCE_CONTACT;
Chris Wren99f963e2014-05-28 16:52:42 -040062
John Spurlockae641c92014-06-30 18:11:40 -040063 public static final int[] ALL_DAYS = { Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY,
64 Calendar.WEDNESDAY, Calendar.THURSDAY, Calendar.FRIDAY, Calendar.SATURDAY };
65 public static final int[] WEEKNIGHT_DAYS = { Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY,
66 Calendar.WEDNESDAY, Calendar.THURSDAY };
John Spurlockb2278d62015-04-07 12:47:12 -040067 public static final int[] WEEKEND_DAYS = { Calendar.FRIDAY, Calendar.SATURDAY };
John Spurlockae641c92014-06-30 18:11:40 -040068
John Spurlockd9c75db2015-04-28 11:19:13 -040069 public static final int[] MINUTE_BUCKETS = generateMinuteBuckets();
Jason Monk4dd81462014-09-08 10:13:59 -040070 private static final int SECONDS_MS = 1000;
71 private static final int MINUTES_MS = 60 * SECONDS_MS;
Julia Reynolds0842fe82015-10-01 14:34:22 -040072 private static final int DAY_MINUTES = 24 * 60;
John Spurlock530052a2014-11-30 16:26:19 -050073 private static final int ZERO_VALUE_MS = 10 * SECONDS_MS;
Jason Monk4dd81462014-09-08 10:13:59 -040074
John Spurlocka492d1d2015-05-05 18:30:28 -040075 private static final boolean DEFAULT_ALLOW_CALLS = true;
76 private static final boolean DEFAULT_ALLOW_MESSAGES = false;
John Spurlockfc746f82015-04-03 13:47:14 -040077 private static final boolean DEFAULT_ALLOW_REMINDERS = true;
John Spurlock2dac62c2014-09-19 15:29:06 -040078 private static final boolean DEFAULT_ALLOW_EVENTS = true;
John Spurlock1d7d2242015-04-10 08:10:22 -040079 private static final boolean DEFAULT_ALLOW_REPEAT_CALLERS = false;
Julia Reynoldsd5607292016-02-05 15:25:58 -050080 private static final boolean DEFAULT_ALLOW_SCREEN_OFF = true;
Julia Reynolds61721582016-01-05 08:35:25 -050081 private static final boolean DEFAULT_ALLOW_SCREEN_ON = true;
John Spurlock2dac62c2014-09-19 15:29:06 -040082
John Spurlockb2278d62015-04-07 12:47:12 -040083 private static final int XML_VERSION = 2;
John Spurlock056c5192014-04-20 21:52:01 -040084 private static final String ZEN_TAG = "zen";
85 private static final String ZEN_ATT_VERSION = "version";
John Spurlock21258a32015-05-27 18:22:55 -040086 private static final String ZEN_ATT_USER = "user";
John Spurlock056c5192014-04-20 21:52:01 -040087 private static final String ALLOW_TAG = "allow";
88 private static final String ALLOW_ATT_CALLS = "calls";
John Spurlock1d7d2242015-04-10 08:10:22 -040089 private static final String ALLOW_ATT_REPEAT_CALLERS = "repeatCallers";
John Spurlock056c5192014-04-20 21:52:01 -040090 private static final String ALLOW_ATT_MESSAGES = "messages";
Chris Wren99f963e2014-05-28 16:52:42 -040091 private static final String ALLOW_ATT_FROM = "from";
John Spurlocka492d1d2015-05-05 18:30:28 -040092 private static final String ALLOW_ATT_CALLS_FROM = "callsFrom";
93 private static final String ALLOW_ATT_MESSAGES_FROM = "messagesFrom";
John Spurlockfc746f82015-04-03 13:47:14 -040094 private static final String ALLOW_ATT_REMINDERS = "reminders";
John Spurlock2dac62c2014-09-19 15:29:06 -040095 private static final String ALLOW_ATT_EVENTS = "events";
Julia Reynoldsd5607292016-02-05 15:25:58 -050096 private static final String ALLOW_ATT_SCREEN_OFF = "visualScreenOff";
97 private static final String ALLOW_ATT_SCREEN_ON = "visualScreenOn";
John Spurlock056c5192014-04-20 21:52:01 -040098
John Spurlock3b98b3f2014-05-01 09:08:48 -040099 private static final String CONDITION_TAG = "condition";
100 private static final String CONDITION_ATT_COMPONENT = "component";
101 private static final String CONDITION_ATT_ID = "id";
John Spurlock4db0d982014-08-13 09:19:03 -0400102 private static final String CONDITION_ATT_SUMMARY = "summary";
103 private static final String CONDITION_ATT_LINE1 = "line1";
104 private static final String CONDITION_ATT_LINE2 = "line2";
105 private static final String CONDITION_ATT_ICON = "icon";
106 private static final String CONDITION_ATT_STATE = "state";
107 private static final String CONDITION_ATT_FLAGS = "flags";
John Spurlock3b98b3f2014-05-01 09:08:48 -0400108
John Spurlockb2278d62015-04-07 12:47:12 -0400109 private static final String MANUAL_TAG = "manual";
110 private static final String AUTOMATIC_TAG = "automatic";
111
John Spurlockd60258f2015-04-30 09:30:52 -0400112 private static final String RULE_ATT_ID = "ruleId";
John Spurlockb2278d62015-04-07 12:47:12 -0400113 private static final String RULE_ATT_ENABLED = "enabled";
114 private static final String RULE_ATT_SNOOZING = "snoozing";
115 private static final String RULE_ATT_NAME = "name";
116 private static final String RULE_ATT_COMPONENT = "component";
117 private static final String RULE_ATT_ZEN = "zen";
118 private static final String RULE_ATT_CONDITION_ID = "conditionId";
Julia Reynolds56106ff2015-09-30 14:42:53 -0400119 private static final String RULE_ATT_CREATION_TIME = "creationTime";
John Spurlock856edeb2014-06-01 20:36:47 -0400120
John Spurlocka492d1d2015-05-05 18:30:28 -0400121 public boolean allowCalls = DEFAULT_ALLOW_CALLS;
John Spurlock1d7d2242015-04-10 08:10:22 -0400122 public boolean allowRepeatCallers = DEFAULT_ALLOW_REPEAT_CALLERS;
John Spurlocka492d1d2015-05-05 18:30:28 -0400123 public boolean allowMessages = DEFAULT_ALLOW_MESSAGES;
John Spurlockfc746f82015-04-03 13:47:14 -0400124 public boolean allowReminders = DEFAULT_ALLOW_REMINDERS;
John Spurlock2dac62c2014-09-19 15:29:06 -0400125 public boolean allowEvents = DEFAULT_ALLOW_EVENTS;
John Spurlocka492d1d2015-05-05 18:30:28 -0400126 public int allowCallsFrom = DEFAULT_SOURCE;
127 public int allowMessagesFrom = DEFAULT_SOURCE;
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -0700128 public int user = UserHandle.USER_SYSTEM;
Julia Reynoldsd5607292016-02-05 15:25:58 -0500129 public boolean allowWhenScreenOff = DEFAULT_ALLOW_SCREEN_OFF;
130 public boolean allowWhenScreenOn = DEFAULT_ALLOW_SCREEN_ON;
John Spurlock056c5192014-04-20 21:52:01 -0400131
John Spurlockb2278d62015-04-07 12:47:12 -0400132 public ZenRule manualRule;
133 public ArrayMap<String, ZenRule> automaticRules = new ArrayMap<>();
John Spurlock056c5192014-04-20 21:52:01 -0400134
135 public ZenModeConfig() { }
136
137 public ZenModeConfig(Parcel source) {
138 allowCalls = source.readInt() == 1;
John Spurlock1d7d2242015-04-10 08:10:22 -0400139 allowRepeatCallers = source.readInt() == 1;
John Spurlock056c5192014-04-20 21:52:01 -0400140 allowMessages = source.readInt() == 1;
John Spurlockfc746f82015-04-03 13:47:14 -0400141 allowReminders = source.readInt() == 1;
John Spurlock2dac62c2014-09-19 15:29:06 -0400142 allowEvents = source.readInt() == 1;
John Spurlocka492d1d2015-05-05 18:30:28 -0400143 allowCallsFrom = source.readInt();
144 allowMessagesFrom = source.readInt();
John Spurlock21258a32015-05-27 18:22:55 -0400145 user = source.readInt();
John Spurlockb2278d62015-04-07 12:47:12 -0400146 manualRule = source.readParcelable(null);
147 final int len = source.readInt();
148 if (len > 0) {
149 final String[] ids = new String[len];
150 final ZenRule[] rules = new ZenRule[len];
151 source.readStringArray(ids);
152 source.readTypedArray(rules, ZenRule.CREATOR);
153 for (int i = 0; i < len; i++) {
154 automaticRules.put(ids[i], rules[i]);
155 }
156 }
Julia Reynoldsd5607292016-02-05 15:25:58 -0500157 allowWhenScreenOff = source.readInt() == 1;
158 allowWhenScreenOn = source.readInt() == 1;
John Spurlock056c5192014-04-20 21:52:01 -0400159 }
160
161 @Override
162 public void writeToParcel(Parcel dest, int flags) {
163 dest.writeInt(allowCalls ? 1 : 0);
John Spurlock1d7d2242015-04-10 08:10:22 -0400164 dest.writeInt(allowRepeatCallers ? 1 : 0);
John Spurlock056c5192014-04-20 21:52:01 -0400165 dest.writeInt(allowMessages ? 1 : 0);
John Spurlockfc746f82015-04-03 13:47:14 -0400166 dest.writeInt(allowReminders ? 1 : 0);
John Spurlock2dac62c2014-09-19 15:29:06 -0400167 dest.writeInt(allowEvents ? 1 : 0);
John Spurlocka492d1d2015-05-05 18:30:28 -0400168 dest.writeInt(allowCallsFrom);
169 dest.writeInt(allowMessagesFrom);
John Spurlock21258a32015-05-27 18:22:55 -0400170 dest.writeInt(user);
John Spurlockb2278d62015-04-07 12:47:12 -0400171 dest.writeParcelable(manualRule, 0);
172 if (!automaticRules.isEmpty()) {
173 final int len = automaticRules.size();
174 final String[] ids = new String[len];
175 final ZenRule[] rules = new ZenRule[len];
176 for (int i = 0; i < len; i++) {
177 ids[i] = automaticRules.keyAt(i);
178 rules[i] = automaticRules.valueAt(i);
179 }
180 dest.writeInt(len);
181 dest.writeStringArray(ids);
182 dest.writeTypedArray(rules, 0);
183 } else {
184 dest.writeInt(0);
185 }
Julia Reynoldsd5607292016-02-05 15:25:58 -0500186 dest.writeInt(allowWhenScreenOff ? 1 : 0);
187 dest.writeInt(allowWhenScreenOn ? 1 : 0);
John Spurlock056c5192014-04-20 21:52:01 -0400188 }
189
190 @Override
191 public String toString() {
192 return new StringBuilder(ZenModeConfig.class.getSimpleName()).append('[')
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500193 .append("user=").append(user)
194 .append(",allowCalls=").append(allowCalls)
195 .append(",allowRepeatCallers=").append(allowRepeatCallers)
196 .append(",allowMessages=").append(allowMessages)
197 .append(",allowCallsFrom=").append(sourceToString(allowCallsFrom))
198 .append(",allowMessagesFrom=").append(sourceToString(allowMessagesFrom))
199 .append(",allowReminders=").append(allowReminders)
200 .append(",allowEvents=").append(allowEvents)
Julia Reynoldsd5607292016-02-05 15:25:58 -0500201 .append(",allowWhenScreenOff=").append(allowWhenScreenOff)
202 .append(",allowWhenScreenOn=").append(allowWhenScreenOn)
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500203 .append(",automaticRules=").append(automaticRules)
204 .append(",manualRule=").append(manualRule)
205 .append(']').toString();
John Spurlock056c5192014-04-20 21:52:01 -0400206 }
207
John Spurlock21258a32015-05-27 18:22:55 -0400208 private Diff diff(ZenModeConfig to) {
209 final Diff d = new Diff();
210 if (to == null) {
211 return d.addLine("config", "delete");
212 }
213 if (user != to.user) {
214 d.addLine("user", user, to.user);
215 }
216 if (allowCalls != to.allowCalls) {
217 d.addLine("allowCalls", allowCalls, to.allowCalls);
218 }
219 if (allowRepeatCallers != to.allowRepeatCallers) {
220 d.addLine("allowRepeatCallers", allowRepeatCallers, to.allowRepeatCallers);
221 }
222 if (allowMessages != to.allowMessages) {
223 d.addLine("allowMessages", allowMessages, to.allowMessages);
224 }
225 if (allowCallsFrom != to.allowCallsFrom) {
226 d.addLine("allowCallsFrom", allowCallsFrom, to.allowCallsFrom);
227 }
228 if (allowMessagesFrom != to.allowMessagesFrom) {
229 d.addLine("allowMessagesFrom", allowMessagesFrom, to.allowMessagesFrom);
230 }
231 if (allowReminders != to.allowReminders) {
232 d.addLine("allowReminders", allowReminders, to.allowReminders);
233 }
234 if (allowEvents != to.allowEvents) {
235 d.addLine("allowEvents", allowEvents, to.allowEvents);
236 }
Julia Reynoldsd5607292016-02-05 15:25:58 -0500237 if (allowWhenScreenOff != to.allowWhenScreenOff) {
238 d.addLine("allowWhenScreenOff", allowWhenScreenOff, to.allowWhenScreenOff);
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500239 }
Julia Reynoldsd5607292016-02-05 15:25:58 -0500240 if (allowWhenScreenOn != to.allowWhenScreenOn) {
241 d.addLine("allowWhenScreenOn", allowWhenScreenOn, to.allowWhenScreenOn);
Julia Reynolds61721582016-01-05 08:35:25 -0500242 }
John Spurlock21258a32015-05-27 18:22:55 -0400243 final ArraySet<String> allRules = new ArraySet<>();
244 addKeys(allRules, automaticRules);
245 addKeys(allRules, to.automaticRules);
246 final int N = allRules.size();
247 for (int i = 0; i < N; i++) {
248 final String rule = allRules.valueAt(i);
249 final ZenRule fromRule = automaticRules != null ? automaticRules.get(rule) : null;
250 final ZenRule toRule = to.automaticRules != null ? to.automaticRules.get(rule) : null;
251 ZenRule.appendDiff(d, "automaticRule[" + rule + "]", fromRule, toRule);
252 }
253 ZenRule.appendDiff(d, "manualRule", manualRule, to.manualRule);
254 return d;
255 }
256
257 public static Diff diff(ZenModeConfig from, ZenModeConfig to) {
258 if (from == null) {
259 final Diff d = new Diff();
260 if (to != null) {
261 d.addLine("config", "insert");
262 }
263 return d;
264 }
265 return from.diff(to);
266 }
267
268 private static <T> void addKeys(ArraySet<T> set, ArrayMap<T, ?> map) {
269 if (map != null) {
270 for (int i = 0; i < map.size(); i++) {
271 set.add(map.keyAt(i));
272 }
273 }
274 }
275
John Spurlockb2278d62015-04-07 12:47:12 -0400276 public boolean isValid() {
277 if (!isValidManualRule(manualRule)) return false;
278 final int N = automaticRules.size();
279 for (int i = 0; i < N; i++) {
280 if (!isValidAutomaticRule(automaticRules.valueAt(i))) return false;
281 }
282 return true;
283 }
284
285 private static boolean isValidManualRule(ZenRule rule) {
286 return rule == null || Global.isValidZenMode(rule.zenMode) && sameCondition(rule);
287 }
288
289 private static boolean isValidAutomaticRule(ZenRule rule) {
290 return rule != null && !TextUtils.isEmpty(rule.name) && Global.isValidZenMode(rule.zenMode)
291 && rule.conditionId != null && sameCondition(rule);
292 }
293
294 private static boolean sameCondition(ZenRule rule) {
295 if (rule == null) return false;
296 if (rule.conditionId == null) {
297 return rule.condition == null;
298 } else {
299 return rule.condition == null || rule.conditionId.equals(rule.condition.id);
300 }
301 }
302
John Spurlockd9c75db2015-04-28 11:19:13 -0400303 private static int[] generateMinuteBuckets() {
304 final int maxHrs = 12;
305 final int[] buckets = new int[maxHrs + 3];
306 buckets[0] = 15;
307 buckets[1] = 30;
308 buckets[2] = 45;
309 for (int i = 1; i <= maxHrs; i++) {
310 buckets[2 + i] = 60 * i;
311 }
312 return buckets;
313 }
314
Chris Wren99f963e2014-05-28 16:52:42 -0400315 public static String sourceToString(int source) {
316 switch (source) {
317 case SOURCE_ANYONE:
318 return "anyone";
319 case SOURCE_CONTACT:
320 return "contacts";
321 case SOURCE_STAR:
322 return "stars";
323 default:
324 return "UNKNOWN";
325 }
326 }
327
John Spurlock056c5192014-04-20 21:52:01 -0400328 @Override
329 public boolean equals(Object o) {
330 if (!(o instanceof ZenModeConfig)) return false;
331 if (o == this) return true;
332 final ZenModeConfig other = (ZenModeConfig) o;
333 return other.allowCalls == allowCalls
John Spurlock1d7d2242015-04-10 08:10:22 -0400334 && other.allowRepeatCallers == allowRepeatCallers
John Spurlock056c5192014-04-20 21:52:01 -0400335 && other.allowMessages == allowMessages
John Spurlocka492d1d2015-05-05 18:30:28 -0400336 && other.allowCallsFrom == allowCallsFrom
337 && other.allowMessagesFrom == allowMessagesFrom
John Spurlockfc746f82015-04-03 13:47:14 -0400338 && other.allowReminders == allowReminders
John Spurlock2dac62c2014-09-19 15:29:06 -0400339 && other.allowEvents == allowEvents
Julia Reynoldsd5607292016-02-05 15:25:58 -0500340 && other.allowWhenScreenOff == allowWhenScreenOff
341 && other.allowWhenScreenOn == allowWhenScreenOn
John Spurlock21258a32015-05-27 18:22:55 -0400342 && other.user == user
John Spurlockb2278d62015-04-07 12:47:12 -0400343 && Objects.equals(other.automaticRules, automaticRules)
344 && Objects.equals(other.manualRule, manualRule);
John Spurlock056c5192014-04-20 21:52:01 -0400345 }
346
347 @Override
348 public int hashCode() {
John Spurlocka492d1d2015-05-05 18:30:28 -0400349 return Objects.hash(allowCalls, allowRepeatCallers, allowMessages, allowCallsFrom,
Julia Reynoldsd5607292016-02-05 15:25:58 -0500350 allowMessagesFrom, allowReminders, allowEvents, allowWhenScreenOff,
351 allowWhenScreenOn,
352 user, automaticRules, manualRule);
John Spurlock056c5192014-04-20 21:52:01 -0400353 }
354
John Spurlockb2278d62015-04-07 12:47:12 -0400355 private static String toDayList(int[] days) {
356 if (days == null || days.length == 0) return "";
357 final StringBuilder sb = new StringBuilder();
358 for (int i = 0; i < days.length; i++) {
359 if (i > 0) sb.append('.');
360 sb.append(days[i]);
361 }
362 return sb.toString();
John Spurlockae641c92014-06-30 18:11:40 -0400363 }
364
John Spurlockb2278d62015-04-07 12:47:12 -0400365 private static int[] tryParseDayList(String dayList, String sep) {
366 if (dayList == null) return null;
367 final String[] tokens = dayList.split(sep);
John Spurlockae641c92014-06-30 18:11:40 -0400368 if (tokens.length == 0) return null;
369 final int[] rt = new int[tokens.length];
370 for (int i = 0; i < tokens.length; i++) {
371 final int day = tryParseInt(tokens[i], -1);
372 if (day == -1) return null;
373 rt[i] = day;
374 }
375 return rt;
376 }
377
378 private static int tryParseInt(String value, int defValue) {
379 if (TextUtils.isEmpty(value)) return defValue;
380 try {
381 return Integer.valueOf(value);
382 } catch (NumberFormatException e) {
383 return defValue;
384 }
John Spurlock056c5192014-04-20 21:52:01 -0400385 }
386
John Spurlockd60258f2015-04-30 09:30:52 -0400387 private static long tryParseLong(String value, long defValue) {
388 if (TextUtils.isEmpty(value)) return defValue;
389 try {
390 return Long.valueOf(value);
391 } catch (NumberFormatException e) {
392 return defValue;
393 }
394 }
395
John Spurlockb2278d62015-04-07 12:47:12 -0400396 public static ZenModeConfig readXml(XmlPullParser parser, Migration migration)
John Spurlock056c5192014-04-20 21:52:01 -0400397 throws XmlPullParserException, IOException {
398 int type = parser.getEventType();
399 if (type != XmlPullParser.START_TAG) return null;
400 String tag = parser.getName();
401 if (!ZEN_TAG.equals(tag)) return null;
402 final ZenModeConfig rt = new ZenModeConfig();
John Spurlockae641c92014-06-30 18:11:40 -0400403 final int version = safeInt(parser, ZEN_ATT_VERSION, XML_VERSION);
John Spurlockb2278d62015-04-07 12:47:12 -0400404 if (version == 1) {
405 final XmlV1 v1 = XmlV1.readXml(parser);
406 return migration.migrate(v1);
407 }
John Spurlock21258a32015-05-27 18:22:55 -0400408 rt.user = safeInt(parser, ZEN_ATT_USER, rt.user);
John Spurlock056c5192014-04-20 21:52:01 -0400409 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
410 tag = parser.getName();
John Spurlock3b98b3f2014-05-01 09:08:48 -0400411 if (type == XmlPullParser.END_TAG && ZEN_TAG.equals(tag)) {
John Spurlock3b98b3f2014-05-01 09:08:48 -0400412 return rt;
413 }
John Spurlock056c5192014-04-20 21:52:01 -0400414 if (type == XmlPullParser.START_TAG) {
415 if (ALLOW_TAG.equals(tag)) {
416 rt.allowCalls = safeBoolean(parser, ALLOW_ATT_CALLS, false);
John Spurlock1d7d2242015-04-10 08:10:22 -0400417 rt.allowRepeatCallers = safeBoolean(parser, ALLOW_ATT_REPEAT_CALLERS,
418 DEFAULT_ALLOW_REPEAT_CALLERS);
John Spurlock056c5192014-04-20 21:52:01 -0400419 rt.allowMessages = safeBoolean(parser, ALLOW_ATT_MESSAGES, false);
John Spurlockfc746f82015-04-03 13:47:14 -0400420 rt.allowReminders = safeBoolean(parser, ALLOW_ATT_REMINDERS,
421 DEFAULT_ALLOW_REMINDERS);
John Spurlock2dac62c2014-09-19 15:29:06 -0400422 rt.allowEvents = safeBoolean(parser, ALLOW_ATT_EVENTS, DEFAULT_ALLOW_EVENTS);
John Spurlocka492d1d2015-05-05 18:30:28 -0400423 final int from = safeInt(parser, ALLOW_ATT_FROM, -1);
424 final int callsFrom = safeInt(parser, ALLOW_ATT_CALLS_FROM, -1);
425 final int messagesFrom = safeInt(parser, ALLOW_ATT_MESSAGES_FROM, -1);
426 if (isValidSource(callsFrom) && isValidSource(messagesFrom)) {
427 rt.allowCallsFrom = callsFrom;
428 rt.allowMessagesFrom = messagesFrom;
429 } else if (isValidSource(from)) {
430 Slog.i(TAG, "Migrating existing shared 'from': " + sourceToString(from));
431 rt.allowCallsFrom = from;
432 rt.allowMessagesFrom = from;
433 } else {
434 rt.allowCallsFrom = DEFAULT_SOURCE;
435 rt.allowMessagesFrom = DEFAULT_SOURCE;
Chris Wren99f963e2014-05-28 16:52:42 -0400436 }
Julia Reynoldsd5607292016-02-05 15:25:58 -0500437 rt.allowWhenScreenOff =
438 safeBoolean(parser, ALLOW_ATT_SCREEN_OFF, DEFAULT_ALLOW_SCREEN_OFF);
439 rt.allowWhenScreenOn =
Julia Reynolds61721582016-01-05 08:35:25 -0500440 safeBoolean(parser, ALLOW_ATT_SCREEN_ON, DEFAULT_ALLOW_SCREEN_ON);
John Spurlockb2278d62015-04-07 12:47:12 -0400441 } else if (MANUAL_TAG.equals(tag)) {
John Spurlock995a7492015-05-28 22:13:03 -0400442 rt.manualRule = readRuleXml(parser);
John Spurlockb2278d62015-04-07 12:47:12 -0400443 } else if (AUTOMATIC_TAG.equals(tag)) {
444 final String id = parser.getAttributeValue(null, RULE_ATT_ID);
John Spurlock995a7492015-05-28 22:13:03 -0400445 final ZenRule automaticRule = readRuleXml(parser);
John Spurlockb2278d62015-04-07 12:47:12 -0400446 if (id != null && automaticRule != null) {
Julia Reynolds56106ff2015-09-30 14:42:53 -0400447 automaticRule.id = id;
John Spurlockb2278d62015-04-07 12:47:12 -0400448 rt.automaticRules.put(id, automaticRule);
John Spurlock4db0d982014-08-13 09:19:03 -0400449 }
John Spurlock056c5192014-04-20 21:52:01 -0400450 }
451 }
452 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400453 throw new IllegalStateException("Failed to reach END_DOCUMENT");
John Spurlock056c5192014-04-20 21:52:01 -0400454 }
455
456 public void writeXml(XmlSerializer out) throws IOException {
457 out.startTag(null, ZEN_TAG);
458 out.attribute(null, ZEN_ATT_VERSION, Integer.toString(XML_VERSION));
John Spurlock21258a32015-05-27 18:22:55 -0400459 out.attribute(null, ZEN_ATT_USER, Integer.toString(user));
John Spurlock056c5192014-04-20 21:52:01 -0400460
461 out.startTag(null, ALLOW_TAG);
462 out.attribute(null, ALLOW_ATT_CALLS, Boolean.toString(allowCalls));
John Spurlock1d7d2242015-04-10 08:10:22 -0400463 out.attribute(null, ALLOW_ATT_REPEAT_CALLERS, Boolean.toString(allowRepeatCallers));
John Spurlock056c5192014-04-20 21:52:01 -0400464 out.attribute(null, ALLOW_ATT_MESSAGES, Boolean.toString(allowMessages));
John Spurlockfc746f82015-04-03 13:47:14 -0400465 out.attribute(null, ALLOW_ATT_REMINDERS, Boolean.toString(allowReminders));
John Spurlock2dac62c2014-09-19 15:29:06 -0400466 out.attribute(null, ALLOW_ATT_EVENTS, Boolean.toString(allowEvents));
John Spurlocka492d1d2015-05-05 18:30:28 -0400467 out.attribute(null, ALLOW_ATT_CALLS_FROM, Integer.toString(allowCallsFrom));
468 out.attribute(null, ALLOW_ATT_MESSAGES_FROM, Integer.toString(allowMessagesFrom));
Julia Reynoldsd5607292016-02-05 15:25:58 -0500469 out.attribute(null, ALLOW_ATT_SCREEN_OFF, Boolean.toString(allowWhenScreenOff));
470 out.attribute(null, ALLOW_ATT_SCREEN_ON, Boolean.toString(allowWhenScreenOn));
John Spurlock056c5192014-04-20 21:52:01 -0400471 out.endTag(null, ALLOW_TAG);
472
John Spurlockb2278d62015-04-07 12:47:12 -0400473 if (manualRule != null) {
474 out.startTag(null, MANUAL_TAG);
475 writeRuleXml(manualRule, out);
476 out.endTag(null, MANUAL_TAG);
John Spurlock056c5192014-04-20 21:52:01 -0400477 }
John Spurlockb2278d62015-04-07 12:47:12 -0400478 final int N = automaticRules.size();
479 for (int i = 0; i < N; i++) {
480 final String id = automaticRules.keyAt(i);
481 final ZenRule automaticRule = automaticRules.valueAt(i);
482 out.startTag(null, AUTOMATIC_TAG);
483 out.attribute(null, RULE_ATT_ID, id);
484 writeRuleXml(automaticRule, out);
485 out.endTag(null, AUTOMATIC_TAG);
John Spurlock856edeb2014-06-01 20:36:47 -0400486 }
John Spurlock056c5192014-04-20 21:52:01 -0400487 out.endTag(null, ZEN_TAG);
488 }
489
John Spurlock995a7492015-05-28 22:13:03 -0400490 public static ZenRule readRuleXml(XmlPullParser parser) {
John Spurlockb2278d62015-04-07 12:47:12 -0400491 final ZenRule rt = new ZenRule();
492 rt.enabled = safeBoolean(parser, RULE_ATT_ENABLED, true);
493 rt.snoozing = safeBoolean(parser, RULE_ATT_SNOOZING, false);
494 rt.name = parser.getAttributeValue(null, RULE_ATT_NAME);
495 final String zen = parser.getAttributeValue(null, RULE_ATT_ZEN);
496 rt.zenMode = tryParseZenMode(zen, -1);
497 if (rt.zenMode == -1) {
498 Slog.w(TAG, "Bad zen mode in rule xml:" + zen);
499 return null;
500 }
501 rt.conditionId = safeUri(parser, RULE_ATT_CONDITION_ID);
502 rt.component = safeComponentName(parser, RULE_ATT_COMPONENT);
Julia Reynolds56106ff2015-09-30 14:42:53 -0400503 rt.creationTime = safeLong(parser, RULE_ATT_CREATION_TIME, 0);
John Spurlockb2278d62015-04-07 12:47:12 -0400504 rt.condition = readConditionXml(parser);
John Spurlockd60258f2015-04-30 09:30:52 -0400505 return rt;
John Spurlockb2278d62015-04-07 12:47:12 -0400506 }
507
508 public static void writeRuleXml(ZenRule rule, XmlSerializer out) throws IOException {
509 out.attribute(null, RULE_ATT_ENABLED, Boolean.toString(rule.enabled));
510 out.attribute(null, RULE_ATT_SNOOZING, Boolean.toString(rule.snoozing));
511 if (rule.name != null) {
512 out.attribute(null, RULE_ATT_NAME, rule.name);
513 }
514 out.attribute(null, RULE_ATT_ZEN, Integer.toString(rule.zenMode));
515 if (rule.component != null) {
516 out.attribute(null, RULE_ATT_COMPONENT, rule.component.flattenToString());
517 }
518 if (rule.conditionId != null) {
519 out.attribute(null, RULE_ATT_CONDITION_ID, rule.conditionId.toString());
520 }
Julia Reynolds56106ff2015-09-30 14:42:53 -0400521 out.attribute(null, RULE_ATT_CREATION_TIME, Long.toString(rule.creationTime));
John Spurlockb2278d62015-04-07 12:47:12 -0400522 if (rule.condition != null) {
523 writeConditionXml(rule.condition, out);
524 }
525 }
526
John Spurlock4db0d982014-08-13 09:19:03 -0400527 public static Condition readConditionXml(XmlPullParser parser) {
528 final Uri id = safeUri(parser, CONDITION_ATT_ID);
John Spurlockb2278d62015-04-07 12:47:12 -0400529 if (id == null) return null;
John Spurlock4db0d982014-08-13 09:19:03 -0400530 final String summary = parser.getAttributeValue(null, CONDITION_ATT_SUMMARY);
531 final String line1 = parser.getAttributeValue(null, CONDITION_ATT_LINE1);
532 final String line2 = parser.getAttributeValue(null, CONDITION_ATT_LINE2);
533 final int icon = safeInt(parser, CONDITION_ATT_ICON, -1);
534 final int state = safeInt(parser, CONDITION_ATT_STATE, -1);
535 final int flags = safeInt(parser, CONDITION_ATT_FLAGS, -1);
536 try {
537 return new Condition(id, summary, line1, line2, icon, state, flags);
538 } catch (IllegalArgumentException e) {
539 Slog.w(TAG, "Unable to read condition xml", e);
540 return null;
541 }
542 }
543
544 public static void writeConditionXml(Condition c, XmlSerializer out) throws IOException {
545 out.attribute(null, CONDITION_ATT_ID, c.id.toString());
546 out.attribute(null, CONDITION_ATT_SUMMARY, c.summary);
547 out.attribute(null, CONDITION_ATT_LINE1, c.line1);
548 out.attribute(null, CONDITION_ATT_LINE2, c.line2);
549 out.attribute(null, CONDITION_ATT_ICON, Integer.toString(c.icon));
550 out.attribute(null, CONDITION_ATT_STATE, Integer.toString(c.state));
551 out.attribute(null, CONDITION_ATT_FLAGS, Integer.toString(c.flags));
552 }
553
John Spurlock056c5192014-04-20 21:52:01 -0400554 public static boolean isValidHour(int val) {
555 return val >= 0 && val < 24;
556 }
557
558 public static boolean isValidMinute(int val) {
559 return val >= 0 && val < 60;
560 }
561
John Spurlocka492d1d2015-05-05 18:30:28 -0400562 private static boolean isValidSource(int source) {
563 return source >= SOURCE_ANYONE && source <= MAX_SOURCE;
564 }
565
John Spurlock056c5192014-04-20 21:52:01 -0400566 private static boolean safeBoolean(XmlPullParser parser, String att, boolean defValue) {
567 final String val = parser.getAttributeValue(null, att);
Julia Reynolds1998ee52016-02-11 13:49:08 -0500568 return safeBoolean(val, defValue);
569 }
570
571 private static boolean safeBoolean(String val, boolean defValue) {
John Spurlock056c5192014-04-20 21:52:01 -0400572 if (TextUtils.isEmpty(val)) return defValue;
573 return Boolean.valueOf(val);
574 }
575
576 private static int safeInt(XmlPullParser parser, String att, int defValue) {
577 final String val = parser.getAttributeValue(null, att);
John Spurlockae641c92014-06-30 18:11:40 -0400578 return tryParseInt(val, defValue);
John Spurlock056c5192014-04-20 21:52:01 -0400579 }
580
John Spurlock3b98b3f2014-05-01 09:08:48 -0400581 private static ComponentName safeComponentName(XmlPullParser parser, String att) {
582 final String val = parser.getAttributeValue(null, att);
583 if (TextUtils.isEmpty(val)) return null;
584 return ComponentName.unflattenFromString(val);
585 }
586
587 private static Uri safeUri(XmlPullParser parser, String att) {
588 final String val = parser.getAttributeValue(null, att);
589 if (TextUtils.isEmpty(val)) return null;
590 return Uri.parse(val);
591 }
592
Julia Reynolds56106ff2015-09-30 14:42:53 -0400593 private static long safeLong(XmlPullParser parser, String att, long defValue) {
594 final String val = parser.getAttributeValue(null, att);
595 return tryParseLong(val, defValue);
596 }
597
John Spurlock056c5192014-04-20 21:52:01 -0400598 @Override
599 public int describeContents() {
600 return 0;
601 }
602
603 public ZenModeConfig copy() {
604 final Parcel parcel = Parcel.obtain();
605 try {
606 writeToParcel(parcel, 0);
607 parcel.setDataPosition(0);
608 return new ZenModeConfig(parcel);
609 } finally {
610 parcel.recycle();
611 }
612 }
613
614 public static final Parcelable.Creator<ZenModeConfig> CREATOR
615 = new Parcelable.Creator<ZenModeConfig>() {
616 @Override
617 public ZenModeConfig createFromParcel(Parcel source) {
618 return new ZenModeConfig(source);
619 }
620
621 @Override
622 public ZenModeConfig[] newArray(int size) {
623 return new ZenModeConfig[size];
624 }
625 };
John Spurlock856edeb2014-06-01 20:36:47 -0400626
John Spurlock1fc476d2015-04-14 16:05:20 -0400627 public Policy toNotificationPolicy() {
628 int priorityCategories = 0;
John Spurlocka492d1d2015-05-05 18:30:28 -0400629 int priorityCallSenders = Policy.PRIORITY_SENDERS_CONTACTS;
630 int priorityMessageSenders = Policy.PRIORITY_SENDERS_CONTACTS;
John Spurlock1fc476d2015-04-14 16:05:20 -0400631 if (allowCalls) {
632 priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS;
633 }
634 if (allowMessages) {
635 priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES;
636 }
637 if (allowEvents) {
638 priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS;
639 }
640 if (allowReminders) {
641 priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS;
642 }
643 if (allowRepeatCallers) {
644 priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS;
645 }
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500646 int suppressedVisualEffects = 0;
Julia Reynoldsd5607292016-02-05 15:25:58 -0500647 if (!allowWhenScreenOff) {
648 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500649 }
Julia Reynoldsd5607292016-02-05 15:25:58 -0500650 if (!allowWhenScreenOn) {
Julia Reynolds61721582016-01-05 08:35:25 -0500651 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_ON;
652 }
John Spurlocka492d1d2015-05-05 18:30:28 -0400653 priorityCallSenders = sourceToPrioritySenders(allowCallsFrom, priorityCallSenders);
654 priorityMessageSenders = sourceToPrioritySenders(allowMessagesFrom, priorityMessageSenders);
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500655 return new Policy(priorityCategories, priorityCallSenders, priorityMessageSenders,
656 suppressedVisualEffects);
John Spurlocka492d1d2015-05-05 18:30:28 -0400657 }
658
659 private static int sourceToPrioritySenders(int source, int def) {
660 switch (source) {
661 case SOURCE_ANYONE: return Policy.PRIORITY_SENDERS_ANY;
662 case SOURCE_CONTACT: return Policy.PRIORITY_SENDERS_CONTACTS;
663 case SOURCE_STAR: return Policy.PRIORITY_SENDERS_STARRED;
664 default: return def;
John Spurlock1fc476d2015-04-14 16:05:20 -0400665 }
John Spurlocka492d1d2015-05-05 18:30:28 -0400666 }
667
668 private static int prioritySendersToSource(int prioritySenders, int def) {
669 switch (prioritySenders) {
670 case Policy.PRIORITY_SENDERS_CONTACTS: return SOURCE_CONTACT;
671 case Policy.PRIORITY_SENDERS_STARRED: return SOURCE_STAR;
672 case Policy.PRIORITY_SENDERS_ANY: return SOURCE_ANYONE;
673 default: return def;
674 }
John Spurlock1fc476d2015-04-14 16:05:20 -0400675 }
676
677 public void applyNotificationPolicy(Policy policy) {
678 if (policy == null) return;
679 allowCalls = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_CALLS) != 0;
680 allowMessages = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MESSAGES) != 0;
681 allowEvents = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_EVENTS) != 0;
682 allowReminders = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REMINDERS) != 0;
683 allowRepeatCallers = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REPEAT_CALLERS)
684 != 0;
John Spurlock80774932015-05-07 17:38:50 -0400685 allowCallsFrom = prioritySendersToSource(policy.priorityCallSenders, allowCallsFrom);
686 allowMessagesFrom = prioritySendersToSource(policy.priorityMessageSenders,
687 allowMessagesFrom);
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500688 if (policy.suppressedVisualEffects != Policy.SUPPRESSED_EFFECTS_UNSET) {
Julia Reynoldsd5607292016-02-05 15:25:58 -0500689 allowWhenScreenOff =
690 (policy.suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_SCREEN_OFF) == 0;
691 allowWhenScreenOn =
Julia Reynolds61721582016-01-05 08:35:25 -0500692 (policy.suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_SCREEN_ON) == 0;
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500693 }
John Spurlock1fc476d2015-04-14 16:05:20 -0400694 }
695
Selim Cinek9c4a7072014-11-21 17:44:34 +0100696 public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle) {
John Spurlockbbde2672015-05-13 15:42:04 -0400697 return toTimeCondition(context, minutesFromNow, userHandle, false /*shortVersion*/);
698 }
699
700 public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle,
701 boolean shortVersion) {
Jason Monk4dd81462014-09-08 10:13:59 -0400702 final long now = System.currentTimeMillis();
703 final long millis = minutesFromNow == 0 ? ZERO_VALUE_MS : minutesFromNow * MINUTES_MS;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400704 return toTimeCondition(context, now + millis, minutesFromNow, userHandle, shortVersion);
Jason Monk4dd81462014-09-08 10:13:59 -0400705 }
706
Julia Reynolds0842fe82015-10-01 14:34:22 -0400707 public static Condition toTimeCondition(Context context, long time, int minutes,
John Spurlockbbde2672015-05-13 15:42:04 -0400708 int userHandle, boolean shortVersion) {
Julia Reynolds0842fe82015-10-01 14:34:22 -0400709 final int num;
710 String summary, line1, line2;
Julia Reynolds8e2d2bb2015-10-01 14:34:22 -0400711 final CharSequence formattedTime = getFormattedTime(context, time, userHandle);
Julia Reynolds0842fe82015-10-01 14:34:22 -0400712 final Resources res = context.getResources();
John Spurlockc90e6fe2014-10-28 11:21:42 -0400713 if (minutes < 60) {
714 // display as minutes
715 num = minutes;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400716 int summaryResId = shortVersion ? R.plurals.zen_mode_duration_minutes_summary_short
John Spurlockbbde2672015-05-13 15:42:04 -0400717 : R.plurals.zen_mode_duration_minutes_summary;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400718 summary = res.getQuantityString(summaryResId, num, num, formattedTime);
719 int line1ResId = shortVersion ? R.plurals.zen_mode_duration_minutes_short
John Spurlockbbde2672015-05-13 15:42:04 -0400720 : R.plurals.zen_mode_duration_minutes;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400721 line1 = res.getQuantityString(line1ResId, num, num, formattedTime);
722 line2 = res.getString(R.string.zen_mode_until, formattedTime);
723 } else if (minutes < DAY_MINUTES) {
John Spurlockc90e6fe2014-10-28 11:21:42 -0400724 // display as hours
725 num = Math.round(minutes / 60f);
Julia Reynolds0842fe82015-10-01 14:34:22 -0400726 int summaryResId = shortVersion ? R.plurals.zen_mode_duration_hours_summary_short
John Spurlockbbde2672015-05-13 15:42:04 -0400727 : R.plurals.zen_mode_duration_hours_summary;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400728 summary = res.getQuantityString(summaryResId, num, num, formattedTime);
729 int line1ResId = shortVersion ? R.plurals.zen_mode_duration_hours_short
John Spurlockbbde2672015-05-13 15:42:04 -0400730 : R.plurals.zen_mode_duration_hours;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400731 line1 = res.getQuantityString(line1ResId, num, num, formattedTime);
732 line2 = res.getString(R.string.zen_mode_until, formattedTime);
733 } else {
734 // display as day/time
735 summary = line1 = line2 = res.getString(R.string.zen_mode_until, formattedTime);
John Spurlockc90e6fe2014-10-28 11:21:42 -0400736 }
Jason Monk4dd81462014-09-08 10:13:59 -0400737 final Uri id = toCountdownConditionId(time);
John Spurlockc90e6fe2014-10-28 11:21:42 -0400738 return new Condition(id, summary, line1, line2, 0, Condition.STATE_TRUE,
Jason Monk4dd81462014-09-08 10:13:59 -0400739 Condition.FLAG_RELEVANT_NOW);
740 }
741
Julia Reynolds0842fe82015-10-01 14:34:22 -0400742 public static Condition toNextAlarmCondition(Context context, long now, long alarm,
743 int userHandle) {
Julia Reynolds8e2d2bb2015-10-01 14:34:22 -0400744 final CharSequence formattedTime = getFormattedTime(context, alarm, userHandle);
Julia Reynolds0842fe82015-10-01 14:34:22 -0400745 final Resources res = context.getResources();
746 final String line1 = res.getString(R.string.zen_mode_alarm, formattedTime);
747 final Uri id = toCountdownConditionId(alarm);
748 return new Condition(id, "", line1, "", 0, Condition.STATE_TRUE,
749 Condition.FLAG_RELEVANT_NOW);
750 }
751
Julia Reynolds8e2d2bb2015-10-01 14:34:22 -0400752 private static CharSequence getFormattedTime(Context context, long time, int userHandle) {
753 String skeleton = "EEE " + (DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma");
754 GregorianCalendar now = new GregorianCalendar();
755 GregorianCalendar endTime = new GregorianCalendar();
756 endTime.setTimeInMillis(time);
757 if (now.get(Calendar.YEAR) == endTime.get(Calendar.YEAR)
758 && now.get(Calendar.MONTH) == endTime.get(Calendar.MONTH)
759 && now.get(Calendar.DATE) == endTime.get(Calendar.DATE)) {
760 skeleton = DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma";
Julia Reynolds0842fe82015-10-01 14:34:22 -0400761 }
762 final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
763 return DateFormat.format(pattern, time);
764 }
765
John Spurlockd60258f2015-04-30 09:30:52 -0400766 // ==== Built-in system conditions ====
767
John Spurlock37bc92c2014-11-03 11:01:51 -0500768 public static final String SYSTEM_AUTHORITY = "android";
John Spurlock4db0d982014-08-13 09:19:03 -0400769
John Spurlockd60258f2015-04-30 09:30:52 -0400770 // ==== Built-in system condition: countdown ====
771
John Spurlock530052a2014-11-30 16:26:19 -0500772 public static final String COUNTDOWN_PATH = "countdown";
John Spurlock856edeb2014-06-01 20:36:47 -0400773
774 public static Uri toCountdownConditionId(long time) {
775 return new Uri.Builder().scheme(Condition.SCHEME)
John Spurlock4db0d982014-08-13 09:19:03 -0400776 .authority(SYSTEM_AUTHORITY)
John Spurlock856edeb2014-06-01 20:36:47 -0400777 .appendPath(COUNTDOWN_PATH)
778 .appendPath(Long.toString(time))
779 .build();
780 }
781
782 public static long tryParseCountdownConditionId(Uri conditionId) {
John Spurlock4db0d982014-08-13 09:19:03 -0400783 if (!Condition.isValidId(conditionId, SYSTEM_AUTHORITY)) return 0;
John Spurlock856edeb2014-06-01 20:36:47 -0400784 if (conditionId.getPathSegments().size() != 2
785 || !COUNTDOWN_PATH.equals(conditionId.getPathSegments().get(0))) return 0;
786 try {
787 return Long.parseLong(conditionId.getPathSegments().get(1));
788 } catch (RuntimeException e) {
789 Slog.w(TAG, "Error parsing countdown condition: " + conditionId, e);
790 return 0;
791 }
792 }
793
794 public static boolean isValidCountdownConditionId(Uri conditionId) {
795 return tryParseCountdownConditionId(conditionId) != 0;
796 }
John Spurlock4db0d982014-08-13 09:19:03 -0400797
John Spurlockd60258f2015-04-30 09:30:52 -0400798 // ==== Built-in system condition: schedule ====
799
John Spurlockb2278d62015-04-07 12:47:12 -0400800 public static final String SCHEDULE_PATH = "schedule";
John Spurlock4db0d982014-08-13 09:19:03 -0400801
John Spurlockd60258f2015-04-30 09:30:52 -0400802 public static Uri toScheduleConditionId(ScheduleInfo schedule) {
803 return new Uri.Builder().scheme(Condition.SCHEME)
804 .authority(SYSTEM_AUTHORITY)
805 .appendPath(SCHEDULE_PATH)
806 .appendQueryParameter("days", toDayList(schedule.days))
807 .appendQueryParameter("start", schedule.startHour + "." + schedule.startMinute)
808 .appendQueryParameter("end", schedule.endHour + "." + schedule.endMinute)
Julia Reynolds1998ee52016-02-11 13:49:08 -0500809 .appendQueryParameter("exitAtAlarm", String.valueOf(schedule.exitAtAlarm))
John Spurlockd60258f2015-04-30 09:30:52 -0400810 .build();
811 }
812
813 public static boolean isValidScheduleConditionId(Uri conditionId) {
814 return tryParseScheduleConditionId(conditionId) != null;
815 }
816
817 public static ScheduleInfo tryParseScheduleConditionId(Uri conditionId) {
818 final boolean isSchedule = conditionId != null
819 && conditionId.getScheme().equals(Condition.SCHEME)
820 && conditionId.getAuthority().equals(ZenModeConfig.SYSTEM_AUTHORITY)
821 && conditionId.getPathSegments().size() == 1
822 && conditionId.getPathSegments().get(0).equals(ZenModeConfig.SCHEDULE_PATH);
823 if (!isSchedule) return null;
824 final int[] start = tryParseHourAndMinute(conditionId.getQueryParameter("start"));
825 final int[] end = tryParseHourAndMinute(conditionId.getQueryParameter("end"));
826 if (start == null || end == null) return null;
827 final ScheduleInfo rt = new ScheduleInfo();
828 rt.days = tryParseDayList(conditionId.getQueryParameter("days"), "\\.");
829 rt.startHour = start[0];
830 rt.startMinute = start[1];
831 rt.endHour = end[0];
832 rt.endMinute = end[1];
Julia Reynolds1998ee52016-02-11 13:49:08 -0500833 rt.exitAtAlarm = safeBoolean(conditionId.getQueryParameter("exitAtAlarm"), false);
John Spurlockd60258f2015-04-30 09:30:52 -0400834 return rt;
835 }
836
Julia Reynoldsa47a27f2015-08-24 08:31:47 -0400837 public static ComponentName getScheduleConditionProvider() {
838 return new ComponentName(SYSTEM_AUTHORITY, "ScheduleConditionProvider");
839 }
840
John Spurlockb2278d62015-04-07 12:47:12 -0400841 public static class ScheduleInfo {
842 public int[] days;
843 public int startHour;
844 public int startMinute;
845 public int endHour;
846 public int endMinute;
Julia Reynolds1998ee52016-02-11 13:49:08 -0500847 public boolean exitAtAlarm;
848 public long nextAlarm;
John Spurlockb2278d62015-04-07 12:47:12 -0400849
850 @Override
851 public int hashCode() {
852 return 0;
853 }
854
855 @Override
856 public boolean equals(Object o) {
857 if (!(o instanceof ScheduleInfo)) return false;
858 final ScheduleInfo other = (ScheduleInfo) o;
859 return toDayList(days).equals(toDayList(other.days))
860 && startHour == other.startHour
861 && startMinute == other.startMinute
862 && endHour == other.endHour
Julia Reynolds1998ee52016-02-11 13:49:08 -0500863 && endMinute == other.endMinute
864 && exitAtAlarm == other.exitAtAlarm;
John Spurlockb2278d62015-04-07 12:47:12 -0400865 }
866
867 public ScheduleInfo copy() {
868 final ScheduleInfo rt = new ScheduleInfo();
869 if (days != null) {
870 rt.days = new int[days.length];
871 System.arraycopy(days, 0, rt.days, 0, days.length);
872 }
873 rt.startHour = startHour;
874 rt.startMinute = startMinute;
875 rt.endHour = endHour;
876 rt.endMinute = endMinute;
Julia Reynolds1998ee52016-02-11 13:49:08 -0500877 rt.exitAtAlarm = exitAtAlarm;
878 rt.nextAlarm = nextAlarm;
John Spurlockb2278d62015-04-07 12:47:12 -0400879 return rt;
880 }
881 }
882
John Spurlockd60258f2015-04-30 09:30:52 -0400883 // ==== Built-in system condition: event ====
884
885 public static final String EVENT_PATH = "event";
886
887 public static Uri toEventConditionId(EventInfo event) {
John Spurlock4db0d982014-08-13 09:19:03 -0400888 return new Uri.Builder().scheme(Condition.SCHEME)
889 .authority(SYSTEM_AUTHORITY)
John Spurlockd60258f2015-04-30 09:30:52 -0400890 .appendPath(EVENT_PATH)
John Spurlock1b8b22b2015-05-20 09:47:13 -0400891 .appendQueryParameter("userId", Long.toString(event.userId))
John Spurlock995a7492015-05-28 22:13:03 -0400892 .appendQueryParameter("calendar", event.calendar != null ? event.calendar : "")
John Spurlockd60258f2015-04-30 09:30:52 -0400893 .appendQueryParameter("reply", Integer.toString(event.reply))
John Spurlock4db0d982014-08-13 09:19:03 -0400894 .build();
895 }
896
John Spurlockd60258f2015-04-30 09:30:52 -0400897 public static boolean isValidEventConditionId(Uri conditionId) {
898 return tryParseEventConditionId(conditionId) != null;
John Spurlockb2278d62015-04-07 12:47:12 -0400899 }
900
John Spurlockd60258f2015-04-30 09:30:52 -0400901 public static EventInfo tryParseEventConditionId(Uri conditionId) {
902 final boolean isEvent = conditionId != null
John Spurlockb2278d62015-04-07 12:47:12 -0400903 && conditionId.getScheme().equals(Condition.SCHEME)
904 && conditionId.getAuthority().equals(ZenModeConfig.SYSTEM_AUTHORITY)
905 && conditionId.getPathSegments().size() == 1
John Spurlockd60258f2015-04-30 09:30:52 -0400906 && conditionId.getPathSegments().get(0).equals(EVENT_PATH);
907 if (!isEvent) return null;
908 final EventInfo rt = new EventInfo();
John Spurlock1b8b22b2015-05-20 09:47:13 -0400909 rt.userId = tryParseInt(conditionId.getQueryParameter("userId"), UserHandle.USER_NULL);
John Spurlock995a7492015-05-28 22:13:03 -0400910 rt.calendar = conditionId.getQueryParameter("calendar");
911 if (TextUtils.isEmpty(rt.calendar) || tryParseLong(rt.calendar, -1L) != -1L) {
912 rt.calendar = null;
913 }
John Spurlockd60258f2015-04-30 09:30:52 -0400914 rt.reply = tryParseInt(conditionId.getQueryParameter("reply"), 0);
John Spurlockb2278d62015-04-07 12:47:12 -0400915 return rt;
John Spurlock4db0d982014-08-13 09:19:03 -0400916 }
917
Julia Reynoldsa47a27f2015-08-24 08:31:47 -0400918 public static ComponentName getEventConditionProvider() {
919 return new ComponentName(SYSTEM_AUTHORITY, "EventConditionProvider");
920 }
921
John Spurlockd60258f2015-04-30 09:30:52 -0400922 public static class EventInfo {
John Spurlockd39af2d2015-05-05 09:49:32 -0400923 public static final int REPLY_ANY_EXCEPT_NO = 0;
924 public static final int REPLY_YES_OR_MAYBE = 1;
John Spurlockd60258f2015-04-30 09:30:52 -0400925 public static final int REPLY_YES = 2;
926
John Spurlock1b8b22b2015-05-20 09:47:13 -0400927 public int userId = UserHandle.USER_NULL; // USER_NULL = unspecified - use current user
John Spurlock995a7492015-05-28 22:13:03 -0400928 public String calendar; // CalendarContract.Calendars.OWNER_ACCOUNT, or null for any
John Spurlockd60258f2015-04-30 09:30:52 -0400929 public int reply;
930
931 @Override
932 public int hashCode() {
933 return 0;
934 }
935
936 @Override
937 public boolean equals(Object o) {
938 if (!(o instanceof EventInfo)) return false;
939 final EventInfo other = (EventInfo) o;
John Spurlock1b8b22b2015-05-20 09:47:13 -0400940 return userId == other.userId
John Spurlock995a7492015-05-28 22:13:03 -0400941 && Objects.equals(calendar, other.calendar)
John Spurlockd60258f2015-04-30 09:30:52 -0400942 && reply == other.reply;
943 }
944
945 public EventInfo copy() {
946 final EventInfo rt = new EventInfo();
John Spurlock1b8b22b2015-05-20 09:47:13 -0400947 rt.userId = userId;
John Spurlockd60258f2015-04-30 09:30:52 -0400948 rt.calendar = calendar;
John Spurlockd60258f2015-04-30 09:30:52 -0400949 rt.reply = reply;
950 return rt;
951 }
John Spurlock1b8b22b2015-05-20 09:47:13 -0400952
953 public static int resolveUserId(int userId) {
954 return userId == UserHandle.USER_NULL ? ActivityManager.getCurrentUser() : userId;
John Spurlock1b8b22b2015-05-20 09:47:13 -0400955 }
John Spurlockd60258f2015-04-30 09:30:52 -0400956 }
957
958 // ==== End built-in system conditions ====
959
John Spurlock4db0d982014-08-13 09:19:03 -0400960 private static int[] tryParseHourAndMinute(String value) {
961 if (TextUtils.isEmpty(value)) return null;
962 final int i = value.indexOf('.');
963 if (i < 1 || i >= value.length() - 1) return null;
964 final int hour = tryParseInt(value.substring(0, i), -1);
965 final int minute = tryParseInt(value.substring(i + 1), -1);
966 return isValidHour(hour) && isValidMinute(minute) ? new int[] { hour, minute } : null;
967 }
968
John Spurlockb2278d62015-04-07 12:47:12 -0400969 private static int tryParseZenMode(String value, int defValue) {
970 final int rt = tryParseInt(value, defValue);
971 return Global.isValidZenMode(rt) ? rt : defValue;
John Spurlock4db0d982014-08-13 09:19:03 -0400972 }
973
Julia Reynolds4fe98d62015-10-06 16:23:41 -0400974 public static String newRuleId() {
John Spurlockb2278d62015-04-07 12:47:12 -0400975 return UUID.randomUUID().toString().replace("-", "");
976 }
977
John Spurlockb2278d62015-04-07 12:47:12 -0400978 public static String getConditionSummary(Context context, ZenModeConfig config,
John Spurlockbbde2672015-05-13 15:42:04 -0400979 int userHandle, boolean shortVersion) {
980 return getConditionLine(context, config, userHandle, false /*useLine1*/, shortVersion);
John Spurlockb2278d62015-04-07 12:47:12 -0400981 }
982
983 private static String getConditionLine(Context context, ZenModeConfig config,
John Spurlockbbde2672015-05-13 15:42:04 -0400984 int userHandle, boolean useLine1, boolean shortVersion) {
John Spurlockb2278d62015-04-07 12:47:12 -0400985 if (config == null) return "";
986 if (config.manualRule != null) {
987 final Uri id = config.manualRule.conditionId;
988 if (id == null) {
989 return context.getString(com.android.internal.R.string.zen_mode_forever);
990 }
991 final long time = tryParseCountdownConditionId(id);
992 Condition c = config.manualRule.condition;
993 if (time > 0) {
994 final long now = System.currentTimeMillis();
995 final long span = time - now;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400996 c = toTimeCondition(context, time, Math.round(span / (float) MINUTES_MS),
997 userHandle, shortVersion);
John Spurlockb2278d62015-04-07 12:47:12 -0400998 }
999 final String rt = c == null ? "" : useLine1 ? c.line1 : c.summary;
1000 return TextUtils.isEmpty(rt) ? "" : rt;
1001 }
1002 String summary = "";
1003 for (ZenRule automaticRule : config.automaticRules.values()) {
John Spurlock39581cc2015-04-10 11:59:01 -04001004 if (automaticRule.isAutomaticActive()) {
John Spurlockb2278d62015-04-07 12:47:12 -04001005 if (summary.isEmpty()) {
1006 summary = automaticRule.name;
1007 } else {
1008 summary = context.getResources()
1009 .getString(R.string.zen_mode_rule_name_combination, summary,
1010 automaticRule.name);
1011 }
1012 }
1013 }
1014 return summary;
1015 }
1016
1017 public static class ZenRule implements Parcelable {
1018 public boolean enabled;
1019 public boolean snoozing; // user manually disabled this instance
1020 public String name; // required for automatic (unique)
1021 public int zenMode;
1022 public Uri conditionId; // required for automatic
1023 public Condition condition; // optional
1024 public ComponentName component; // optional
Julia Reynolds56106ff2015-09-30 14:42:53 -04001025 public String id; // required for automatic (unique)
1026 public long creationTime; // required for automatic
John Spurlockb2278d62015-04-07 12:47:12 -04001027
1028 public ZenRule() { }
1029
1030 public ZenRule(Parcel source) {
1031 enabled = source.readInt() == 1;
1032 snoozing = source.readInt() == 1;
1033 if (source.readInt() == 1) {
1034 name = source.readString();
1035 }
1036 zenMode = source.readInt();
1037 conditionId = source.readParcelable(null);
1038 condition = source.readParcelable(null);
1039 component = source.readParcelable(null);
Julia Reynolds56106ff2015-09-30 14:42:53 -04001040 if (source.readInt() == 1) {
1041 id = source.readString();
1042 }
1043 creationTime = source.readLong();
John Spurlockb2278d62015-04-07 12:47:12 -04001044 }
John Spurlock4db0d982014-08-13 09:19:03 -04001045
1046 @Override
John Spurlockb2278d62015-04-07 12:47:12 -04001047 public int describeContents() {
John Spurlock4db0d982014-08-13 09:19:03 -04001048 return 0;
1049 }
1050
1051 @Override
John Spurlockb2278d62015-04-07 12:47:12 -04001052 public void writeToParcel(Parcel dest, int flags) {
1053 dest.writeInt(enabled ? 1 : 0);
1054 dest.writeInt(snoozing ? 1 : 0);
1055 if (name != null) {
1056 dest.writeInt(1);
1057 dest.writeString(name);
1058 } else {
1059 dest.writeInt(0);
1060 }
1061 dest.writeInt(zenMode);
1062 dest.writeParcelable(conditionId, 0);
1063 dest.writeParcelable(condition, 0);
1064 dest.writeParcelable(component, 0);
Julia Reynolds56106ff2015-09-30 14:42:53 -04001065 if (id != null) {
1066 dest.writeInt(1);
1067 dest.writeString(id);
1068 } else {
1069 dest.writeInt(0);
1070 }
1071 dest.writeLong(creationTime);
John Spurlockb2278d62015-04-07 12:47:12 -04001072 }
1073
1074 @Override
1075 public String toString() {
1076 return new StringBuilder(ZenRule.class.getSimpleName()).append('[')
1077 .append("enabled=").append(enabled)
1078 .append(",snoozing=").append(snoozing)
1079 .append(",name=").append(name)
1080 .append(",zenMode=").append(Global.zenModeToString(zenMode))
1081 .append(",conditionId=").append(conditionId)
1082 .append(",condition=").append(condition)
1083 .append(",component=").append(component)
Julia Reynolds56106ff2015-09-30 14:42:53 -04001084 .append(",id=").append(id)
1085 .append(",creationTime=").append(creationTime)
John Spurlockb2278d62015-04-07 12:47:12 -04001086 .append(']').toString();
1087 }
1088
John Spurlock21258a32015-05-27 18:22:55 -04001089 private static void appendDiff(Diff d, String item, ZenRule from, ZenRule to) {
1090 if (d == null) return;
1091 if (from == null) {
1092 if (to != null) {
1093 d.addLine(item, "insert");
1094 }
1095 return;
1096 }
1097 from.appendDiff(d, item, to);
1098 }
1099
1100 private void appendDiff(Diff d, String item, ZenRule to) {
1101 if (to == null) {
1102 d.addLine(item, "delete");
1103 return;
1104 }
1105 if (enabled != to.enabled) {
1106 d.addLine(item, "enabled", enabled, to.enabled);
1107 }
1108 if (snoozing != to.snoozing) {
1109 d.addLine(item, "snoozing", snoozing, to.snoozing);
1110 }
1111 if (!Objects.equals(name, to.name)) {
1112 d.addLine(item, "name", name, to.name);
1113 }
1114 if (zenMode != to.zenMode) {
1115 d.addLine(item, "zenMode", zenMode, to.zenMode);
1116 }
1117 if (!Objects.equals(conditionId, to.conditionId)) {
1118 d.addLine(item, "conditionId", conditionId, to.conditionId);
1119 }
1120 if (!Objects.equals(condition, to.condition)) {
1121 d.addLine(item, "condition", condition, to.condition);
1122 }
1123 if (!Objects.equals(component, to.component)) {
1124 d.addLine(item, "component", component, to.component);
1125 }
Julia Reynolds56106ff2015-09-30 14:42:53 -04001126 if (!Objects.equals(id, to.id)) {
1127 d.addLine(item, "id", id, to.id);
1128 }
Julia Reynoldsc8e54e82015-11-30 16:43:05 -05001129 if (creationTime != to.creationTime) {
Julia Reynolds56106ff2015-09-30 14:42:53 -04001130 d.addLine(item, "creationTime", creationTime, to.creationTime);
1131 }
John Spurlock21258a32015-05-27 18:22:55 -04001132 }
1133
John Spurlockb2278d62015-04-07 12:47:12 -04001134 @Override
John Spurlock4db0d982014-08-13 09:19:03 -04001135 public boolean equals(Object o) {
John Spurlockb2278d62015-04-07 12:47:12 -04001136 if (!(o instanceof ZenRule)) return false;
1137 if (o == this) return true;
1138 final ZenRule other = (ZenRule) o;
1139 return other.enabled == enabled
1140 && other.snoozing == snoozing
1141 && Objects.equals(other.name, name)
1142 && other.zenMode == zenMode
1143 && Objects.equals(other.conditionId, conditionId)
1144 && Objects.equals(other.condition, condition)
Julia Reynolds56106ff2015-09-30 14:42:53 -04001145 && Objects.equals(other.component, component)
1146 && Objects.equals(other.id, id)
1147 && other.creationTime == creationTime;
John Spurlockb2278d62015-04-07 12:47:12 -04001148 }
1149
1150 @Override
1151 public int hashCode() {
1152 return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
Julia Reynolds56106ff2015-09-30 14:42:53 -04001153 component, id, creationTime);
John Spurlockb2278d62015-04-07 12:47:12 -04001154 }
1155
John Spurlock39581cc2015-04-10 11:59:01 -04001156 public boolean isAutomaticActive() {
1157 return enabled && !snoozing && component != null && isTrueOrUnknown();
1158 }
1159
John Spurlockb2278d62015-04-07 12:47:12 -04001160 public boolean isTrueOrUnknown() {
John Spurlock39581cc2015-04-10 11:59:01 -04001161 return condition != null && (condition.state == Condition.STATE_TRUE
1162 || condition.state == Condition.STATE_UNKNOWN);
John Spurlockb2278d62015-04-07 12:47:12 -04001163 }
1164
1165 public static final Parcelable.Creator<ZenRule> CREATOR
1166 = new Parcelable.Creator<ZenRule>() {
1167 @Override
1168 public ZenRule createFromParcel(Parcel source) {
1169 return new ZenRule(source);
1170 }
1171 @Override
1172 public ZenRule[] newArray(int size) {
1173 return new ZenRule[size];
1174 }
1175 };
1176 }
1177
1178 // Legacy config
1179 public static final class XmlV1 {
1180 public static final String SLEEP_MODE_NIGHTS = "nights";
1181 public static final String SLEEP_MODE_WEEKNIGHTS = "weeknights";
1182 public static final String SLEEP_MODE_DAYS_PREFIX = "days:";
1183
1184 private static final String EXIT_CONDITION_TAG = "exitCondition";
1185 private static final String EXIT_CONDITION_ATT_COMPONENT = "component";
1186 private static final String SLEEP_TAG = "sleep";
1187 private static final String SLEEP_ATT_MODE = "mode";
1188 private static final String SLEEP_ATT_NONE = "none";
1189
1190 private static final String SLEEP_ATT_START_HR = "startHour";
1191 private static final String SLEEP_ATT_START_MIN = "startMin";
1192 private static final String SLEEP_ATT_END_HR = "endHour";
1193 private static final String SLEEP_ATT_END_MIN = "endMin";
1194
1195 public boolean allowCalls;
1196 public boolean allowMessages;
1197 public boolean allowReminders = DEFAULT_ALLOW_REMINDERS;
1198 public boolean allowEvents = DEFAULT_ALLOW_EVENTS;
1199 public int allowFrom = SOURCE_ANYONE;
1200
1201 public String sleepMode; // nights, weeknights, days:1,2,3 Calendar.days
1202 public int sleepStartHour; // 0-23
1203 public int sleepStartMinute; // 0-59
1204 public int sleepEndHour;
1205 public int sleepEndMinute;
1206 public boolean sleepNone; // false = priority, true = none
1207 public ComponentName[] conditionComponents;
1208 public Uri[] conditionIds;
1209 public Condition exitCondition; // manual exit condition
1210 public ComponentName exitConditionComponent; // manual exit condition component
1211
1212 private static boolean isValidSleepMode(String sleepMode) {
1213 return sleepMode == null || sleepMode.equals(SLEEP_MODE_NIGHTS)
1214 || sleepMode.equals(SLEEP_MODE_WEEKNIGHTS) || tryParseDays(sleepMode) != null;
1215 }
1216
1217 public static int[] tryParseDays(String sleepMode) {
1218 if (sleepMode == null) return null;
1219 sleepMode = sleepMode.trim();
1220 if (SLEEP_MODE_NIGHTS.equals(sleepMode)) return ALL_DAYS;
1221 if (SLEEP_MODE_WEEKNIGHTS.equals(sleepMode)) return WEEKNIGHT_DAYS;
1222 if (!sleepMode.startsWith(SLEEP_MODE_DAYS_PREFIX)) return null;
1223 if (sleepMode.equals(SLEEP_MODE_DAYS_PREFIX)) return null;
1224 return tryParseDayList(sleepMode.substring(SLEEP_MODE_DAYS_PREFIX.length()), ",");
1225 }
1226
1227 public static XmlV1 readXml(XmlPullParser parser)
1228 throws XmlPullParserException, IOException {
1229 int type;
1230 String tag;
1231 XmlV1 rt = new XmlV1();
1232 final ArrayList<ComponentName> conditionComponents = new ArrayList<ComponentName>();
1233 final ArrayList<Uri> conditionIds = new ArrayList<Uri>();
1234 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
1235 tag = parser.getName();
1236 if (type == XmlPullParser.END_TAG && ZEN_TAG.equals(tag)) {
1237 if (!conditionComponents.isEmpty()) {
1238 rt.conditionComponents = conditionComponents
1239 .toArray(new ComponentName[conditionComponents.size()]);
1240 rt.conditionIds = conditionIds.toArray(new Uri[conditionIds.size()]);
1241 }
1242 return rt;
1243 }
1244 if (type == XmlPullParser.START_TAG) {
1245 if (ALLOW_TAG.equals(tag)) {
1246 rt.allowCalls = safeBoolean(parser, ALLOW_ATT_CALLS, false);
1247 rt.allowMessages = safeBoolean(parser, ALLOW_ATT_MESSAGES, false);
1248 rt.allowReminders = safeBoolean(parser, ALLOW_ATT_REMINDERS,
1249 DEFAULT_ALLOW_REMINDERS);
1250 rt.allowEvents = safeBoolean(parser, ALLOW_ATT_EVENTS,
1251 DEFAULT_ALLOW_EVENTS);
1252 rt.allowFrom = safeInt(parser, ALLOW_ATT_FROM, SOURCE_ANYONE);
1253 if (rt.allowFrom < SOURCE_ANYONE || rt.allowFrom > MAX_SOURCE) {
1254 throw new IndexOutOfBoundsException("bad source in config:"
1255 + rt.allowFrom);
1256 }
1257 } else if (SLEEP_TAG.equals(tag)) {
1258 final String mode = parser.getAttributeValue(null, SLEEP_ATT_MODE);
1259 rt.sleepMode = isValidSleepMode(mode)? mode : null;
1260 rt.sleepNone = safeBoolean(parser, SLEEP_ATT_NONE, false);
1261 final int startHour = safeInt(parser, SLEEP_ATT_START_HR, 0);
1262 final int startMinute = safeInt(parser, SLEEP_ATT_START_MIN, 0);
1263 final int endHour = safeInt(parser, SLEEP_ATT_END_HR, 0);
1264 final int endMinute = safeInt(parser, SLEEP_ATT_END_MIN, 0);
1265 rt.sleepStartHour = isValidHour(startHour) ? startHour : 0;
1266 rt.sleepStartMinute = isValidMinute(startMinute) ? startMinute : 0;
1267 rt.sleepEndHour = isValidHour(endHour) ? endHour : 0;
1268 rt.sleepEndMinute = isValidMinute(endMinute) ? endMinute : 0;
1269 } else if (CONDITION_TAG.equals(tag)) {
1270 final ComponentName component =
1271 safeComponentName(parser, CONDITION_ATT_COMPONENT);
1272 final Uri conditionId = safeUri(parser, CONDITION_ATT_ID);
1273 if (component != null && conditionId != null) {
1274 conditionComponents.add(component);
1275 conditionIds.add(conditionId);
1276 }
1277 } else if (EXIT_CONDITION_TAG.equals(tag)) {
1278 rt.exitCondition = readConditionXml(parser);
1279 if (rt.exitCondition != null) {
1280 rt.exitConditionComponent =
1281 safeComponentName(parser, EXIT_CONDITION_ATT_COMPONENT);
1282 }
1283 }
1284 }
1285 }
1286 throw new IllegalStateException("Failed to reach END_DOCUMENT");
John Spurlock4db0d982014-08-13 09:19:03 -04001287 }
1288 }
John Spurlock530052a2014-11-30 16:26:19 -05001289
John Spurlockb2278d62015-04-07 12:47:12 -04001290 public interface Migration {
1291 ZenModeConfig migrate(XmlV1 v1);
1292 }
John Spurlock1fc476d2015-04-14 16:05:20 -04001293
John Spurlock21258a32015-05-27 18:22:55 -04001294 public static class Diff {
1295 private final ArrayList<String> lines = new ArrayList<>();
1296
1297 @Override
1298 public String toString() {
1299 final StringBuilder sb = new StringBuilder("Diff[");
1300 final int N = lines.size();
1301 for (int i = 0; i < N; i++) {
1302 if (i > 0) {
1303 sb.append(',');
1304 }
1305 sb.append(lines.get(i));
1306 }
1307 return sb.append(']').toString();
1308 }
1309
1310 private Diff addLine(String item, String action) {
1311 lines.add(item + ":" + action);
1312 return this;
1313 }
1314
1315 public Diff addLine(String item, String subitem, Object from, Object to) {
1316 return addLine(item + "." + subitem, from, to);
1317 }
1318
1319 public Diff addLine(String item, Object from, Object to) {
1320 return addLine(item, from + "->" + to);
1321 }
1322 }
1323
John Spurlock056c5192014-04-20 21:52:01 -04001324}