blob: 5546e803342bd71289f627a26a27f979061ffedc [file] [log] [blame]
John Spurlock056c5192014-04-20 21:52:01 -04001/**
2 * Copyright (c) 2014, The Android Open Source Project
3 *
Julia Reynolds206c7e92016-09-15 10:38:03 -04004 * Licensed under the Apache License, 2.0 (the "License");
John Spurlock056c5192014-04-20 21:52:01 -04005 * 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
Julia Reynolds1f580572018-04-27 14:48:36 -040019import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
20import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
21import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
22
John Spurlock1b8b22b2015-05-20 09:47:13 -040023import android.app.ActivityManager;
Beverlya5effb42018-02-01 17:08:35 -050024import android.app.AlarmManager;
Beverly925cde82018-01-23 09:31:23 -050025import android.app.NotificationManager;
John Spurlock1fc476d2015-04-14 16:05:20 -040026import android.app.NotificationManager.Policy;
John Spurlock3b98b3f2014-05-01 09:08:48 -040027import android.content.ComponentName;
John Spurlockc90e6fe2014-10-28 11:21:42 -040028import android.content.Context;
Julia Reynolds44ad6ff2016-07-06 09:47:45 -040029import android.content.pm.ApplicationInfo;
30import android.content.pm.PackageManager;
Jason Monk4dd81462014-09-08 10:13:59 -040031import android.content.res.Resources;
John Spurlock3b98b3f2014-05-01 09:08:48 -040032import android.net.Uri;
John Spurlock056c5192014-04-20 21:52:01 -040033import android.os.Parcel;
34import android.os.Parcelable;
John Spurlock1b8b22b2015-05-20 09:47:13 -040035import android.os.UserHandle;
John Spurlockb2278d62015-04-07 12:47:12 -040036import android.provider.Settings.Global;
John Spurlock056c5192014-04-20 21:52:01 -040037import android.text.TextUtils;
John Spurlockc90e6fe2014-10-28 11:21:42 -040038import android.text.format.DateFormat;
John Spurlockb2278d62015-04-07 12:47:12 -040039import android.util.ArrayMap;
40import android.util.ArraySet;
John Spurlock856edeb2014-06-01 20:36:47 -040041import android.util.Slog;
Kweku Adams99546332018-01-24 17:03:50 -080042import android.util.proto.ProtoOutputStream;
John Spurlock056c5192014-04-20 21:52:01 -040043
John Spurlockb2278d62015-04-07 12:47:12 -040044import com.android.internal.R;
45
John Spurlock056c5192014-04-20 21:52:01 -040046import org.xmlpull.v1.XmlPullParser;
47import org.xmlpull.v1.XmlPullParserException;
48import org.xmlpull.v1.XmlSerializer;
49
50import java.io.IOException;
John Spurlock3b98b3f2014-05-01 09:08:48 -040051import java.util.ArrayList;
Julia Reynolds7f733082016-04-26 14:19:19 -040052import java.util.Arrays;
John Spurlockae641c92014-06-30 18:11:40 -040053import java.util.Calendar;
Julia Reynoldsfe58f1f2016-07-19 10:18:32 -040054import java.util.Date;
Julia Reynolds8e2d2bb2015-10-01 14:34:22 -040055import java.util.GregorianCalendar;
Beverly26eba872017-12-04 16:30:11 -050056import java.util.List;
John Spurlockc90e6fe2014-10-28 11:21:42 -040057import java.util.Locale;
John Spurlock056c5192014-04-20 21:52:01 -040058import java.util.Objects;
Beverlybe6d3522017-11-20 11:01:59 -050059import java.util.TimeZone;
John Spurlockb2278d62015-04-07 12:47:12 -040060import java.util.UUID;
John Spurlockc90e6fe2014-10-28 11:21:42 -040061
John Spurlock056c5192014-04-20 21:52:01 -040062/**
63 * Persisted configuration for zen mode.
64 *
65 * @hide
66 */
67public class ZenModeConfig implements Parcelable {
John Spurlock856edeb2014-06-01 20:36:47 -040068 private static String TAG = "ZenModeConfig";
John Spurlock056c5192014-04-20 21:52:01 -040069
Chris Wren99f963e2014-05-28 16:52:42 -040070 public static final int SOURCE_ANYONE = 0;
71 public static final int SOURCE_CONTACT = 1;
72 public static final int SOURCE_STAR = 2;
73 public static final int MAX_SOURCE = SOURCE_STAR;
John Spurlocka492d1d2015-05-05 18:30:28 -040074 private static final int DEFAULT_SOURCE = SOURCE_CONTACT;
Julia Reynolds1f580572018-04-27 14:48:36 -040075 private static final int DEFAULT_CALLS_SOURCE = SOURCE_STAR;
Chris Wren99f963e2014-05-28 16:52:42 -040076
Beverly26eba872017-12-04 16:30:11 -050077 public static final String EVENTS_DEFAULT_RULE_ID = "EVENTS_DEFAULT_RULE";
78 public static final String EVERY_NIGHT_DEFAULT_RULE_ID = "EVERY_NIGHT_DEFAULT_RULE";
79 public static final List<String> DEFAULT_RULE_IDS = Arrays.asList(EVERY_NIGHT_DEFAULT_RULE_ID,
80 EVENTS_DEFAULT_RULE_ID);
81
John Spurlockae641c92014-06-30 18:11:40 -040082 public static final int[] ALL_DAYS = { Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY,
83 Calendar.WEDNESDAY, Calendar.THURSDAY, Calendar.FRIDAY, Calendar.SATURDAY };
John Spurlockae641c92014-06-30 18:11:40 -040084
John Spurlockd9c75db2015-04-28 11:19:13 -040085 public static final int[] MINUTE_BUCKETS = generateMinuteBuckets();
Jason Monk4dd81462014-09-08 10:13:59 -040086 private static final int SECONDS_MS = 1000;
87 private static final int MINUTES_MS = 60 * SECONDS_MS;
Julia Reynolds0842fe82015-10-01 14:34:22 -040088 private static final int DAY_MINUTES = 24 * 60;
John Spurlock530052a2014-11-30 16:26:19 -050089 private static final int ZERO_VALUE_MS = 10 * SECONDS_MS;
Jason Monk4dd81462014-09-08 10:13:59 -040090
Julia Reynoldsc4e5ecf2018-04-11 11:33:32 -040091 // Default allow categories set in readXml() from default_zen_mode_config.xml,
92 // fallback/upgrade values:
Beverly04216872017-09-28 10:55:32 -040093 private static final boolean DEFAULT_ALLOW_ALARMS = true;
Beverlyd6964762018-02-16 14:07:03 -050094 private static final boolean DEFAULT_ALLOW_MEDIA = true;
95 private static final boolean DEFAULT_ALLOW_SYSTEM = false;
Julia Reynolds10a7cc12018-05-03 10:56:24 -040096 private static final boolean DEFAULT_ALLOW_CALLS = true;
John Spurlocka492d1d2015-05-05 18:30:28 -040097 private static final boolean DEFAULT_ALLOW_MESSAGES = false;
Beverly04216872017-09-28 10:55:32 -040098 private static final boolean DEFAULT_ALLOW_REMINDERS = false;
99 private static final boolean DEFAULT_ALLOW_EVENTS = false;
Julia Reynolds10a7cc12018-05-03 10:56:24 -0400100 private static final boolean DEFAULT_ALLOW_REPEAT_CALLERS = true;
Beverly86d076f2018-04-17 14:44:52 -0400101 private static final boolean DEFAULT_CHANNELS_BYPASSING_DND = false;
Julia Reynolds1f580572018-04-27 14:48:36 -0400102 private static final int DEFAULT_SUPPRESSED_VISUAL_EFFECTS = 0;
John Spurlock2dac62c2014-09-19 15:29:06 -0400103
Julia Reynolds1f580572018-04-27 14:48:36 -0400104 public static final int XML_VERSION = 8;
Julia Reynoldsd1bf5f02017-07-11 10:39:58 -0400105 public static final String ZEN_TAG = "zen";
John Spurlock056c5192014-04-20 21:52:01 -0400106 private static final String ZEN_ATT_VERSION = "version";
John Spurlock21258a32015-05-27 18:22:55 -0400107 private static final String ZEN_ATT_USER = "user";
John Spurlock056c5192014-04-20 21:52:01 -0400108 private static final String ALLOW_TAG = "allow";
Beverly04216872017-09-28 10:55:32 -0400109 private static final String ALLOW_ATT_ALARMS = "alarms";
Beverlyd6964762018-02-16 14:07:03 -0500110 private static final String ALLOW_ATT_MEDIA = "media";
111 private static final String ALLOW_ATT_SYSTEM = "system";
John Spurlock056c5192014-04-20 21:52:01 -0400112 private static final String ALLOW_ATT_CALLS = "calls";
John Spurlock1d7d2242015-04-10 08:10:22 -0400113 private static final String ALLOW_ATT_REPEAT_CALLERS = "repeatCallers";
John Spurlock056c5192014-04-20 21:52:01 -0400114 private static final String ALLOW_ATT_MESSAGES = "messages";
Chris Wren99f963e2014-05-28 16:52:42 -0400115 private static final String ALLOW_ATT_FROM = "from";
John Spurlocka492d1d2015-05-05 18:30:28 -0400116 private static final String ALLOW_ATT_CALLS_FROM = "callsFrom";
117 private static final String ALLOW_ATT_MESSAGES_FROM = "messagesFrom";
John Spurlockfc746f82015-04-03 13:47:14 -0400118 private static final String ALLOW_ATT_REMINDERS = "reminders";
John Spurlock2dac62c2014-09-19 15:29:06 -0400119 private static final String ALLOW_ATT_EVENTS = "events";
Julia Reynoldsd5607292016-02-05 15:25:58 -0500120 private static final String ALLOW_ATT_SCREEN_OFF = "visualScreenOff";
121 private static final String ALLOW_ATT_SCREEN_ON = "visualScreenOn";
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500122 private static final String DISALLOW_TAG = "disallow";
123 private static final String DISALLOW_ATT_VISUAL_EFFECTS = "visualEffects";
Beverly86d076f2018-04-17 14:44:52 -0400124 private static final String STATE_TAG = "state";
125 private static final String STATE_ATT_CHANNELS_BYPASSING_DND = "areChannelsBypassingDnd";
John Spurlock056c5192014-04-20 21:52:01 -0400126
John Spurlock3b98b3f2014-05-01 09:08:48 -0400127 private static final String CONDITION_ATT_ID = "id";
John Spurlock4db0d982014-08-13 09:19:03 -0400128 private static final String CONDITION_ATT_SUMMARY = "summary";
129 private static final String CONDITION_ATT_LINE1 = "line1";
130 private static final String CONDITION_ATT_LINE2 = "line2";
131 private static final String CONDITION_ATT_ICON = "icon";
132 private static final String CONDITION_ATT_STATE = "state";
133 private static final String CONDITION_ATT_FLAGS = "flags";
John Spurlock3b98b3f2014-05-01 09:08:48 -0400134
John Spurlockb2278d62015-04-07 12:47:12 -0400135 private static final String MANUAL_TAG = "manual";
136 private static final String AUTOMATIC_TAG = "automatic";
137
John Spurlockd60258f2015-04-30 09:30:52 -0400138 private static final String RULE_ATT_ID = "ruleId";
John Spurlockb2278d62015-04-07 12:47:12 -0400139 private static final String RULE_ATT_ENABLED = "enabled";
140 private static final String RULE_ATT_SNOOZING = "snoozing";
141 private static final String RULE_ATT_NAME = "name";
142 private static final String RULE_ATT_COMPONENT = "component";
143 private static final String RULE_ATT_ZEN = "zen";
144 private static final String RULE_ATT_CONDITION_ID = "conditionId";
Julia Reynolds56106ff2015-09-30 14:42:53 -0400145 private static final String RULE_ATT_CREATION_TIME = "creationTime";
Julia Reynolds44ad6ff2016-07-06 09:47:45 -0400146 private static final String RULE_ATT_ENABLER = "enabler";
John Spurlock856edeb2014-06-01 20:36:47 -0400147
Beverly04216872017-09-28 10:55:32 -0400148 public boolean allowAlarms = DEFAULT_ALLOW_ALARMS;
Beverlyd6964762018-02-16 14:07:03 -0500149 public boolean allowMedia = DEFAULT_ALLOW_MEDIA;
150 public boolean allowSystem = DEFAULT_ALLOW_SYSTEM;
John Spurlocka492d1d2015-05-05 18:30:28 -0400151 public boolean allowCalls = DEFAULT_ALLOW_CALLS;
John Spurlock1d7d2242015-04-10 08:10:22 -0400152 public boolean allowRepeatCallers = DEFAULT_ALLOW_REPEAT_CALLERS;
John Spurlocka492d1d2015-05-05 18:30:28 -0400153 public boolean allowMessages = DEFAULT_ALLOW_MESSAGES;
John Spurlockfc746f82015-04-03 13:47:14 -0400154 public boolean allowReminders = DEFAULT_ALLOW_REMINDERS;
John Spurlock2dac62c2014-09-19 15:29:06 -0400155 public boolean allowEvents = DEFAULT_ALLOW_EVENTS;
Julia Reynolds1f580572018-04-27 14:48:36 -0400156 public int allowCallsFrom = DEFAULT_CALLS_SOURCE;
John Spurlocka492d1d2015-05-05 18:30:28 -0400157 public int allowMessagesFrom = DEFAULT_SOURCE;
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -0700158 public int user = UserHandle.USER_SYSTEM;
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500159 public int suppressedVisualEffects = DEFAULT_SUPPRESSED_VISUAL_EFFECTS;
Beverly86d076f2018-04-17 14:44:52 -0400160 public boolean areChannelsBypassingDnd = DEFAULT_CHANNELS_BYPASSING_DND;
Beverly3bae4e52018-02-07 12:32:02 -0500161 public int version;
John Spurlock056c5192014-04-20 21:52:01 -0400162
John Spurlockb2278d62015-04-07 12:47:12 -0400163 public ZenRule manualRule;
164 public ArrayMap<String, ZenRule> automaticRules = new ArrayMap<>();
John Spurlock056c5192014-04-20 21:52:01 -0400165
166 public ZenModeConfig() { }
167
168 public ZenModeConfig(Parcel source) {
169 allowCalls = source.readInt() == 1;
John Spurlock1d7d2242015-04-10 08:10:22 -0400170 allowRepeatCallers = source.readInt() == 1;
John Spurlock056c5192014-04-20 21:52:01 -0400171 allowMessages = source.readInt() == 1;
John Spurlockfc746f82015-04-03 13:47:14 -0400172 allowReminders = source.readInt() == 1;
John Spurlock2dac62c2014-09-19 15:29:06 -0400173 allowEvents = source.readInt() == 1;
John Spurlocka492d1d2015-05-05 18:30:28 -0400174 allowCallsFrom = source.readInt();
175 allowMessagesFrom = source.readInt();
John Spurlock21258a32015-05-27 18:22:55 -0400176 user = source.readInt();
John Spurlockb2278d62015-04-07 12:47:12 -0400177 manualRule = source.readParcelable(null);
178 final int len = source.readInt();
179 if (len > 0) {
180 final String[] ids = new String[len];
181 final ZenRule[] rules = new ZenRule[len];
182 source.readStringArray(ids);
183 source.readTypedArray(rules, ZenRule.CREATOR);
184 for (int i = 0; i < len; i++) {
185 automaticRules.put(ids[i], rules[i]);
186 }
187 }
Beverly04216872017-09-28 10:55:32 -0400188 allowAlarms = source.readInt() == 1;
Beverlyd6964762018-02-16 14:07:03 -0500189 allowMedia = source.readInt() == 1;
190 allowSystem = source.readInt() == 1;
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500191 suppressedVisualEffects = source.readInt();
Beverly86d076f2018-04-17 14:44:52 -0400192 areChannelsBypassingDnd = source.readInt() == 1;
John Spurlock056c5192014-04-20 21:52:01 -0400193 }
194
195 @Override
196 public void writeToParcel(Parcel dest, int flags) {
197 dest.writeInt(allowCalls ? 1 : 0);
John Spurlock1d7d2242015-04-10 08:10:22 -0400198 dest.writeInt(allowRepeatCallers ? 1 : 0);
John Spurlock056c5192014-04-20 21:52:01 -0400199 dest.writeInt(allowMessages ? 1 : 0);
John Spurlockfc746f82015-04-03 13:47:14 -0400200 dest.writeInt(allowReminders ? 1 : 0);
John Spurlock2dac62c2014-09-19 15:29:06 -0400201 dest.writeInt(allowEvents ? 1 : 0);
John Spurlocka492d1d2015-05-05 18:30:28 -0400202 dest.writeInt(allowCallsFrom);
203 dest.writeInt(allowMessagesFrom);
John Spurlock21258a32015-05-27 18:22:55 -0400204 dest.writeInt(user);
John Spurlockb2278d62015-04-07 12:47:12 -0400205 dest.writeParcelable(manualRule, 0);
206 if (!automaticRules.isEmpty()) {
207 final int len = automaticRules.size();
208 final String[] ids = new String[len];
209 final ZenRule[] rules = new ZenRule[len];
210 for (int i = 0; i < len; i++) {
211 ids[i] = automaticRules.keyAt(i);
212 rules[i] = automaticRules.valueAt(i);
213 }
214 dest.writeInt(len);
215 dest.writeStringArray(ids);
216 dest.writeTypedArray(rules, 0);
217 } else {
218 dest.writeInt(0);
219 }
Beverly04216872017-09-28 10:55:32 -0400220 dest.writeInt(allowAlarms ? 1 : 0);
Beverlyd6964762018-02-16 14:07:03 -0500221 dest.writeInt(allowMedia ? 1 : 0);
222 dest.writeInt(allowSystem ? 1 : 0);
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500223 dest.writeInt(suppressedVisualEffects);
Beverly86d076f2018-04-17 14:44:52 -0400224 dest.writeInt(areChannelsBypassingDnd ? 1 : 0);
John Spurlock056c5192014-04-20 21:52:01 -0400225 }
226
227 @Override
228 public String toString() {
229 return new StringBuilder(ZenModeConfig.class.getSimpleName()).append('[')
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500230 .append("user=").append(user)
Beverly04216872017-09-28 10:55:32 -0400231 .append(",allowAlarms=").append(allowAlarms)
Beverlyd6964762018-02-16 14:07:03 -0500232 .append(",allowMedia=").append(allowMedia)
233 .append(",allowSystem=").append(allowSystem)
Beverly04216872017-09-28 10:55:32 -0400234 .append(",allowReminders=").append(allowReminders)
235 .append(",allowEvents=").append(allowEvents)
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500236 .append(",allowCalls=").append(allowCalls)
237 .append(",allowRepeatCallers=").append(allowRepeatCallers)
238 .append(",allowMessages=").append(allowMessages)
239 .append(",allowCallsFrom=").append(sourceToString(allowCallsFrom))
240 .append(",allowMessagesFrom=").append(sourceToString(allowMessagesFrom))
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500241 .append(",suppressedVisualEffects=").append(suppressedVisualEffects)
Beverly86d076f2018-04-17 14:44:52 -0400242 .append(",areChannelsBypassingDnd=").append(areChannelsBypassingDnd)
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500243 .append(",automaticRules=").append(automaticRules)
244 .append(",manualRule=").append(manualRule)
245 .append(']').toString();
John Spurlock056c5192014-04-20 21:52:01 -0400246 }
247
John Spurlock21258a32015-05-27 18:22:55 -0400248 private Diff diff(ZenModeConfig to) {
249 final Diff d = new Diff();
250 if (to == null) {
251 return d.addLine("config", "delete");
252 }
253 if (user != to.user) {
254 d.addLine("user", user, to.user);
255 }
Beverly04216872017-09-28 10:55:32 -0400256 if (allowAlarms != to.allowAlarms) {
257 d.addLine("allowAlarms", allowAlarms, to.allowAlarms);
258 }
Beverlyd6964762018-02-16 14:07:03 -0500259 if (allowMedia != to.allowMedia) {
260 d.addLine("allowMedia", allowMedia, to.allowMedia);
261 }
262 if (allowSystem != to.allowSystem) {
263 d.addLine("allowSystem", allowSystem, to.allowSystem);
Beverly04216872017-09-28 10:55:32 -0400264 }
John Spurlock21258a32015-05-27 18:22:55 -0400265 if (allowCalls != to.allowCalls) {
266 d.addLine("allowCalls", allowCalls, to.allowCalls);
267 }
Beverly04216872017-09-28 10:55:32 -0400268 if (allowReminders != to.allowReminders) {
269 d.addLine("allowReminders", allowReminders, to.allowReminders);
270 }
271 if (allowEvents != to.allowEvents) {
272 d.addLine("allowEvents", allowEvents, to.allowEvents);
273 }
John Spurlock21258a32015-05-27 18:22:55 -0400274 if (allowRepeatCallers != to.allowRepeatCallers) {
275 d.addLine("allowRepeatCallers", allowRepeatCallers, to.allowRepeatCallers);
276 }
277 if (allowMessages != to.allowMessages) {
278 d.addLine("allowMessages", allowMessages, to.allowMessages);
279 }
280 if (allowCallsFrom != to.allowCallsFrom) {
281 d.addLine("allowCallsFrom", allowCallsFrom, to.allowCallsFrom);
282 }
283 if (allowMessagesFrom != to.allowMessagesFrom) {
284 d.addLine("allowMessagesFrom", allowMessagesFrom, to.allowMessagesFrom);
285 }
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500286 if (suppressedVisualEffects != to.suppressedVisualEffects) {
287 d.addLine("suppressedVisualEffects", suppressedVisualEffects,
288 to.suppressedVisualEffects);
289 }
John Spurlock21258a32015-05-27 18:22:55 -0400290 final ArraySet<String> allRules = new ArraySet<>();
291 addKeys(allRules, automaticRules);
292 addKeys(allRules, to.automaticRules);
293 final int N = allRules.size();
294 for (int i = 0; i < N; i++) {
295 final String rule = allRules.valueAt(i);
296 final ZenRule fromRule = automaticRules != null ? automaticRules.get(rule) : null;
297 final ZenRule toRule = to.automaticRules != null ? to.automaticRules.get(rule) : null;
298 ZenRule.appendDiff(d, "automaticRule[" + rule + "]", fromRule, toRule);
299 }
300 ZenRule.appendDiff(d, "manualRule", manualRule, to.manualRule);
Beverly86d076f2018-04-17 14:44:52 -0400301
302 if (areChannelsBypassingDnd != to.areChannelsBypassingDnd) {
303 d.addLine("areChannelsBypassingDnd", areChannelsBypassingDnd,
304 to.areChannelsBypassingDnd);
305 }
John Spurlock21258a32015-05-27 18:22:55 -0400306 return d;
307 }
308
309 public static Diff diff(ZenModeConfig from, ZenModeConfig to) {
310 if (from == null) {
311 final Diff d = new Diff();
312 if (to != null) {
313 d.addLine("config", "insert");
314 }
315 return d;
316 }
317 return from.diff(to);
318 }
319
320 private static <T> void addKeys(ArraySet<T> set, ArrayMap<T, ?> map) {
321 if (map != null) {
322 for (int i = 0; i < map.size(); i++) {
323 set.add(map.keyAt(i));
324 }
325 }
326 }
327
John Spurlockb2278d62015-04-07 12:47:12 -0400328 public boolean isValid() {
329 if (!isValidManualRule(manualRule)) return false;
330 final int N = automaticRules.size();
331 for (int i = 0; i < N; i++) {
332 if (!isValidAutomaticRule(automaticRules.valueAt(i))) return false;
333 }
334 return true;
335 }
336
337 private static boolean isValidManualRule(ZenRule rule) {
338 return rule == null || Global.isValidZenMode(rule.zenMode) && sameCondition(rule);
339 }
340
341 private static boolean isValidAutomaticRule(ZenRule rule) {
342 return rule != null && !TextUtils.isEmpty(rule.name) && Global.isValidZenMode(rule.zenMode)
343 && rule.conditionId != null && sameCondition(rule);
344 }
345
346 private static boolean sameCondition(ZenRule rule) {
347 if (rule == null) return false;
348 if (rule.conditionId == null) {
349 return rule.condition == null;
350 } else {
351 return rule.condition == null || rule.conditionId.equals(rule.condition.id);
352 }
353 }
354
John Spurlockd9c75db2015-04-28 11:19:13 -0400355 private static int[] generateMinuteBuckets() {
356 final int maxHrs = 12;
357 final int[] buckets = new int[maxHrs + 3];
358 buckets[0] = 15;
359 buckets[1] = 30;
360 buckets[2] = 45;
361 for (int i = 1; i <= maxHrs; i++) {
362 buckets[2 + i] = 60 * i;
363 }
364 return buckets;
365 }
366
Chris Wren99f963e2014-05-28 16:52:42 -0400367 public static String sourceToString(int source) {
368 switch (source) {
369 case SOURCE_ANYONE:
370 return "anyone";
371 case SOURCE_CONTACT:
372 return "contacts";
373 case SOURCE_STAR:
374 return "stars";
375 default:
376 return "UNKNOWN";
377 }
378 }
379
John Spurlock056c5192014-04-20 21:52:01 -0400380 @Override
381 public boolean equals(Object o) {
382 if (!(o instanceof ZenModeConfig)) return false;
383 if (o == this) return true;
384 final ZenModeConfig other = (ZenModeConfig) o;
Beverly04216872017-09-28 10:55:32 -0400385 return other.allowAlarms == allowAlarms
Beverlyd6964762018-02-16 14:07:03 -0500386 && other.allowMedia == allowMedia
387 && other.allowSystem == allowSystem
Beverly04216872017-09-28 10:55:32 -0400388 && other.allowCalls == allowCalls
John Spurlock1d7d2242015-04-10 08:10:22 -0400389 && other.allowRepeatCallers == allowRepeatCallers
John Spurlock056c5192014-04-20 21:52:01 -0400390 && other.allowMessages == allowMessages
John Spurlocka492d1d2015-05-05 18:30:28 -0400391 && other.allowCallsFrom == allowCallsFrom
392 && other.allowMessagesFrom == allowMessagesFrom
John Spurlockfc746f82015-04-03 13:47:14 -0400393 && other.allowReminders == allowReminders
John Spurlock2dac62c2014-09-19 15:29:06 -0400394 && other.allowEvents == allowEvents
John Spurlock21258a32015-05-27 18:22:55 -0400395 && other.user == user
John Spurlockb2278d62015-04-07 12:47:12 -0400396 && Objects.equals(other.automaticRules, automaticRules)
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500397 && Objects.equals(other.manualRule, manualRule)
Beverly86d076f2018-04-17 14:44:52 -0400398 && other.suppressedVisualEffects == suppressedVisualEffects
399 && other.areChannelsBypassingDnd == areChannelsBypassingDnd;
John Spurlock056c5192014-04-20 21:52:01 -0400400 }
401
402 @Override
403 public int hashCode() {
Beverlyd6964762018-02-16 14:07:03 -0500404 return Objects.hash(allowAlarms, allowMedia, allowSystem, allowCalls,
Beverly04216872017-09-28 10:55:32 -0400405 allowRepeatCallers, allowMessages,
406 allowCallsFrom, allowMessagesFrom, allowReminders, allowEvents,
Julia Reynolds1f580572018-04-27 14:48:36 -0400407 user, automaticRules, manualRule,
Beverly86d076f2018-04-17 14:44:52 -0400408 suppressedVisualEffects, areChannelsBypassingDnd);
John Spurlock056c5192014-04-20 21:52:01 -0400409 }
410
John Spurlockb2278d62015-04-07 12:47:12 -0400411 private static String toDayList(int[] days) {
412 if (days == null || days.length == 0) return "";
413 final StringBuilder sb = new StringBuilder();
414 for (int i = 0; i < days.length; i++) {
415 if (i > 0) sb.append('.');
416 sb.append(days[i]);
417 }
418 return sb.toString();
John Spurlockae641c92014-06-30 18:11:40 -0400419 }
420
John Spurlockb2278d62015-04-07 12:47:12 -0400421 private static int[] tryParseDayList(String dayList, String sep) {
422 if (dayList == null) return null;
423 final String[] tokens = dayList.split(sep);
John Spurlockae641c92014-06-30 18:11:40 -0400424 if (tokens.length == 0) return null;
425 final int[] rt = new int[tokens.length];
426 for (int i = 0; i < tokens.length; i++) {
427 final int day = tryParseInt(tokens[i], -1);
428 if (day == -1) return null;
429 rt[i] = day;
430 }
431 return rt;
432 }
433
434 private static int tryParseInt(String value, int defValue) {
435 if (TextUtils.isEmpty(value)) return defValue;
436 try {
Narayan Kamatha09b4d22016-04-15 18:32:45 +0100437 return Integer.parseInt(value);
John Spurlockae641c92014-06-30 18:11:40 -0400438 } catch (NumberFormatException e) {
439 return defValue;
440 }
John Spurlock056c5192014-04-20 21:52:01 -0400441 }
442
John Spurlockd60258f2015-04-30 09:30:52 -0400443 private static long tryParseLong(String value, long defValue) {
444 if (TextUtils.isEmpty(value)) return defValue;
445 try {
Tobias Thierer28532d02016-04-21 14:52:10 +0100446 return Long.parseLong(value);
John Spurlockd60258f2015-04-30 09:30:52 -0400447 } catch (NumberFormatException e) {
448 return defValue;
449 }
450 }
451
Julia Reynolds206c7e92016-09-15 10:38:03 -0400452 public static ZenModeConfig readXml(XmlPullParser parser)
John Spurlock056c5192014-04-20 21:52:01 -0400453 throws XmlPullParserException, IOException {
454 int type = parser.getEventType();
455 if (type != XmlPullParser.START_TAG) return null;
456 String tag = parser.getName();
457 if (!ZEN_TAG.equals(tag)) return null;
458 final ZenModeConfig rt = new ZenModeConfig();
Beverly3bae4e52018-02-07 12:32:02 -0500459 rt.version = safeInt(parser, ZEN_ATT_VERSION, XML_VERSION);
John Spurlock21258a32015-05-27 18:22:55 -0400460 rt.user = safeInt(parser, ZEN_ATT_USER, rt.user);
Julia Reynolds1f580572018-04-27 14:48:36 -0400461 boolean readSuppressedEffects = false;
John Spurlock056c5192014-04-20 21:52:01 -0400462 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
463 tag = parser.getName();
John Spurlock3b98b3f2014-05-01 09:08:48 -0400464 if (type == XmlPullParser.END_TAG && ZEN_TAG.equals(tag)) {
John Spurlock3b98b3f2014-05-01 09:08:48 -0400465 return rt;
466 }
John Spurlock056c5192014-04-20 21:52:01 -0400467 if (type == XmlPullParser.START_TAG) {
468 if (ALLOW_TAG.equals(tag)) {
Beverly04216872017-09-28 10:55:32 -0400469 rt.allowCalls = safeBoolean(parser, ALLOW_ATT_CALLS,
470 DEFAULT_ALLOW_CALLS);
John Spurlock1d7d2242015-04-10 08:10:22 -0400471 rt.allowRepeatCallers = safeBoolean(parser, ALLOW_ATT_REPEAT_CALLERS,
472 DEFAULT_ALLOW_REPEAT_CALLERS);
Beverly04216872017-09-28 10:55:32 -0400473 rt.allowMessages = safeBoolean(parser, ALLOW_ATT_MESSAGES,
474 DEFAULT_ALLOW_MESSAGES);
John Spurlockfc746f82015-04-03 13:47:14 -0400475 rt.allowReminders = safeBoolean(parser, ALLOW_ATT_REMINDERS,
476 DEFAULT_ALLOW_REMINDERS);
John Spurlock2dac62c2014-09-19 15:29:06 -0400477 rt.allowEvents = safeBoolean(parser, ALLOW_ATT_EVENTS, DEFAULT_ALLOW_EVENTS);
John Spurlocka492d1d2015-05-05 18:30:28 -0400478 final int from = safeInt(parser, ALLOW_ATT_FROM, -1);
479 final int callsFrom = safeInt(parser, ALLOW_ATT_CALLS_FROM, -1);
480 final int messagesFrom = safeInt(parser, ALLOW_ATT_MESSAGES_FROM, -1);
481 if (isValidSource(callsFrom) && isValidSource(messagesFrom)) {
482 rt.allowCallsFrom = callsFrom;
483 rt.allowMessagesFrom = messagesFrom;
484 } else if (isValidSource(from)) {
485 Slog.i(TAG, "Migrating existing shared 'from': " + sourceToString(from));
486 rt.allowCallsFrom = from;
487 rt.allowMessagesFrom = from;
488 } else {
Julia Reynolds10a7cc12018-05-03 10:56:24 -0400489 rt.allowCallsFrom = DEFAULT_CALLS_SOURCE;
John Spurlocka492d1d2015-05-05 18:30:28 -0400490 rt.allowMessagesFrom = DEFAULT_SOURCE;
Chris Wren99f963e2014-05-28 16:52:42 -0400491 }
Beverly04216872017-09-28 10:55:32 -0400492 rt.allowAlarms = safeBoolean(parser, ALLOW_ATT_ALARMS, DEFAULT_ALLOW_ALARMS);
Beverlyd6964762018-02-16 14:07:03 -0500493 rt.allowMedia = safeBoolean(parser, ALLOW_ATT_MEDIA,
494 DEFAULT_ALLOW_MEDIA);
495 rt.allowSystem = safeBoolean(parser, ALLOW_ATT_SYSTEM, DEFAULT_ALLOW_SYSTEM);
Julia Reynolds1f580572018-04-27 14:48:36 -0400496
497 // migrate old suppressed visual effects fields, if they still exist in the xml
498 Boolean allowWhenScreenOff = unsafeBoolean(parser, ALLOW_ATT_SCREEN_OFF);
499 if (allowWhenScreenOff != null) {
500 readSuppressedEffects = true;
501 if (allowWhenScreenOff) {
502 rt.suppressedVisualEffects |= SUPPRESSED_EFFECT_LIGHTS
503 | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
504 }
505 }
506 Boolean allowWhenScreenOn = unsafeBoolean(parser, ALLOW_ATT_SCREEN_ON);
507 if (allowWhenScreenOn != null) {
508 readSuppressedEffects = true;
509 if (allowWhenScreenOn) {
510 rt.suppressedVisualEffects |= SUPPRESSED_EFFECT_PEEK;
511 }
512 }
513 if (readSuppressedEffects) {
514 Slog.d(TAG, "Migrated visual effects to " + rt.suppressedVisualEffects);
515 }
516 } else if (DISALLOW_TAG.equals(tag) && !readSuppressedEffects) {
517 // only read from suppressed visual effects field if we haven't just migrated
518 // the values from allowOn/allowOff, lest we wipe out those settings
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500519 rt.suppressedVisualEffects = safeInt(parser, DISALLOW_ATT_VISUAL_EFFECTS,
520 DEFAULT_SUPPRESSED_VISUAL_EFFECTS);
John Spurlockb2278d62015-04-07 12:47:12 -0400521 } else if (MANUAL_TAG.equals(tag)) {
John Spurlock995a7492015-05-28 22:13:03 -0400522 rt.manualRule = readRuleXml(parser);
John Spurlockb2278d62015-04-07 12:47:12 -0400523 } else if (AUTOMATIC_TAG.equals(tag)) {
524 final String id = parser.getAttributeValue(null, RULE_ATT_ID);
John Spurlock995a7492015-05-28 22:13:03 -0400525 final ZenRule automaticRule = readRuleXml(parser);
John Spurlockb2278d62015-04-07 12:47:12 -0400526 if (id != null && automaticRule != null) {
Julia Reynolds56106ff2015-09-30 14:42:53 -0400527 automaticRule.id = id;
John Spurlockb2278d62015-04-07 12:47:12 -0400528 rt.automaticRules.put(id, automaticRule);
John Spurlock4db0d982014-08-13 09:19:03 -0400529 }
Beverly86d076f2018-04-17 14:44:52 -0400530 } else if (STATE_TAG.equals(tag)) {
531 rt.areChannelsBypassingDnd = safeBoolean(parser,
532 STATE_ATT_CHANNELS_BYPASSING_DND, DEFAULT_CHANNELS_BYPASSING_DND);
John Spurlock056c5192014-04-20 21:52:01 -0400533 }
534 }
535 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400536 throw new IllegalStateException("Failed to reach END_DOCUMENT");
John Spurlock056c5192014-04-20 21:52:01 -0400537 }
538
Beverly4e2f76c2018-03-16 15:43:49 -0400539 /**
540 * Writes XML of current ZenModeConfig
541 * @param out serializer
542 * @param version uses XML_VERSION if version is null
543 * @throws IOException
544 */
545 public void writeXml(XmlSerializer out, Integer version) throws IOException {
John Spurlock056c5192014-04-20 21:52:01 -0400546 out.startTag(null, ZEN_TAG);
Beverly4e2f76c2018-03-16 15:43:49 -0400547 out.attribute(null, ZEN_ATT_VERSION, version == null
548 ? Integer.toString(XML_VERSION) : Integer.toString(version));
John Spurlock21258a32015-05-27 18:22:55 -0400549 out.attribute(null, ZEN_ATT_USER, Integer.toString(user));
John Spurlock056c5192014-04-20 21:52:01 -0400550 out.startTag(null, ALLOW_TAG);
551 out.attribute(null, ALLOW_ATT_CALLS, Boolean.toString(allowCalls));
John Spurlock1d7d2242015-04-10 08:10:22 -0400552 out.attribute(null, ALLOW_ATT_REPEAT_CALLERS, Boolean.toString(allowRepeatCallers));
John Spurlock056c5192014-04-20 21:52:01 -0400553 out.attribute(null, ALLOW_ATT_MESSAGES, Boolean.toString(allowMessages));
John Spurlockfc746f82015-04-03 13:47:14 -0400554 out.attribute(null, ALLOW_ATT_REMINDERS, Boolean.toString(allowReminders));
John Spurlock2dac62c2014-09-19 15:29:06 -0400555 out.attribute(null, ALLOW_ATT_EVENTS, Boolean.toString(allowEvents));
John Spurlocka492d1d2015-05-05 18:30:28 -0400556 out.attribute(null, ALLOW_ATT_CALLS_FROM, Integer.toString(allowCallsFrom));
557 out.attribute(null, ALLOW_ATT_MESSAGES_FROM, Integer.toString(allowMessagesFrom));
Beverly04216872017-09-28 10:55:32 -0400558 out.attribute(null, ALLOW_ATT_ALARMS, Boolean.toString(allowAlarms));
Beverlyd6964762018-02-16 14:07:03 -0500559 out.attribute(null, ALLOW_ATT_MEDIA, Boolean.toString(allowMedia));
560 out.attribute(null, ALLOW_ATT_SYSTEM, Boolean.toString(allowSystem));
John Spurlock056c5192014-04-20 21:52:01 -0400561 out.endTag(null, ALLOW_TAG);
562
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500563 out.startTag(null, DISALLOW_TAG);
564 out.attribute(null, DISALLOW_ATT_VISUAL_EFFECTS, Integer.toString(suppressedVisualEffects));
565 out.endTag(null, DISALLOW_TAG);
566
John Spurlockb2278d62015-04-07 12:47:12 -0400567 if (manualRule != null) {
568 out.startTag(null, MANUAL_TAG);
569 writeRuleXml(manualRule, out);
570 out.endTag(null, MANUAL_TAG);
John Spurlock056c5192014-04-20 21:52:01 -0400571 }
John Spurlockb2278d62015-04-07 12:47:12 -0400572 final int N = automaticRules.size();
573 for (int i = 0; i < N; i++) {
574 final String id = automaticRules.keyAt(i);
575 final ZenRule automaticRule = automaticRules.valueAt(i);
576 out.startTag(null, AUTOMATIC_TAG);
577 out.attribute(null, RULE_ATT_ID, id);
578 writeRuleXml(automaticRule, out);
579 out.endTag(null, AUTOMATIC_TAG);
John Spurlock856edeb2014-06-01 20:36:47 -0400580 }
Beverly86d076f2018-04-17 14:44:52 -0400581
582 out.startTag(null, STATE_TAG);
583 out.attribute(null, STATE_ATT_CHANNELS_BYPASSING_DND,
584 Boolean.toString(areChannelsBypassingDnd));
585 out.endTag(null, STATE_TAG);
586
John Spurlock056c5192014-04-20 21:52:01 -0400587 out.endTag(null, ZEN_TAG);
588 }
589
John Spurlock995a7492015-05-28 22:13:03 -0400590 public static ZenRule readRuleXml(XmlPullParser parser) {
John Spurlockb2278d62015-04-07 12:47:12 -0400591 final ZenRule rt = new ZenRule();
592 rt.enabled = safeBoolean(parser, RULE_ATT_ENABLED, true);
593 rt.snoozing = safeBoolean(parser, RULE_ATT_SNOOZING, false);
594 rt.name = parser.getAttributeValue(null, RULE_ATT_NAME);
595 final String zen = parser.getAttributeValue(null, RULE_ATT_ZEN);
596 rt.zenMode = tryParseZenMode(zen, -1);
597 if (rt.zenMode == -1) {
598 Slog.w(TAG, "Bad zen mode in rule xml:" + zen);
599 return null;
600 }
601 rt.conditionId = safeUri(parser, RULE_ATT_CONDITION_ID);
602 rt.component = safeComponentName(parser, RULE_ATT_COMPONENT);
Julia Reynolds56106ff2015-09-30 14:42:53 -0400603 rt.creationTime = safeLong(parser, RULE_ATT_CREATION_TIME, 0);
Julia Reynolds44ad6ff2016-07-06 09:47:45 -0400604 rt.enabler = parser.getAttributeValue(null, RULE_ATT_ENABLER);
John Spurlockb2278d62015-04-07 12:47:12 -0400605 rt.condition = readConditionXml(parser);
Beverlybf16c142017-11-27 16:21:34 -0500606
607 // all default rules and user created rules updated to zenMode important interruptions
608 if (rt.zenMode != Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
609 && Condition.isValidId(rt.conditionId, SYSTEM_AUTHORITY)) {
610 Slog.i(TAG, "Updating zenMode of automatic rule " + rt.name);
611 rt.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
612 }
John Spurlockd60258f2015-04-30 09:30:52 -0400613 return rt;
John Spurlockb2278d62015-04-07 12:47:12 -0400614 }
615
616 public static void writeRuleXml(ZenRule rule, XmlSerializer out) throws IOException {
617 out.attribute(null, RULE_ATT_ENABLED, Boolean.toString(rule.enabled));
618 out.attribute(null, RULE_ATT_SNOOZING, Boolean.toString(rule.snoozing));
619 if (rule.name != null) {
620 out.attribute(null, RULE_ATT_NAME, rule.name);
621 }
622 out.attribute(null, RULE_ATT_ZEN, Integer.toString(rule.zenMode));
623 if (rule.component != null) {
624 out.attribute(null, RULE_ATT_COMPONENT, rule.component.flattenToString());
625 }
626 if (rule.conditionId != null) {
627 out.attribute(null, RULE_ATT_CONDITION_ID, rule.conditionId.toString());
628 }
Julia Reynolds56106ff2015-09-30 14:42:53 -0400629 out.attribute(null, RULE_ATT_CREATION_TIME, Long.toString(rule.creationTime));
Julia Reynolds44ad6ff2016-07-06 09:47:45 -0400630 if (rule.enabler != null) {
631 out.attribute(null, RULE_ATT_ENABLER, rule.enabler);
632 }
John Spurlockb2278d62015-04-07 12:47:12 -0400633 if (rule.condition != null) {
634 writeConditionXml(rule.condition, out);
635 }
636 }
637
John Spurlock4db0d982014-08-13 09:19:03 -0400638 public static Condition readConditionXml(XmlPullParser parser) {
639 final Uri id = safeUri(parser, CONDITION_ATT_ID);
John Spurlockb2278d62015-04-07 12:47:12 -0400640 if (id == null) return null;
John Spurlock4db0d982014-08-13 09:19:03 -0400641 final String summary = parser.getAttributeValue(null, CONDITION_ATT_SUMMARY);
642 final String line1 = parser.getAttributeValue(null, CONDITION_ATT_LINE1);
643 final String line2 = parser.getAttributeValue(null, CONDITION_ATT_LINE2);
644 final int icon = safeInt(parser, CONDITION_ATT_ICON, -1);
645 final int state = safeInt(parser, CONDITION_ATT_STATE, -1);
646 final int flags = safeInt(parser, CONDITION_ATT_FLAGS, -1);
647 try {
648 return new Condition(id, summary, line1, line2, icon, state, flags);
649 } catch (IllegalArgumentException e) {
650 Slog.w(TAG, "Unable to read condition xml", e);
651 return null;
652 }
653 }
654
655 public static void writeConditionXml(Condition c, XmlSerializer out) throws IOException {
656 out.attribute(null, CONDITION_ATT_ID, c.id.toString());
657 out.attribute(null, CONDITION_ATT_SUMMARY, c.summary);
658 out.attribute(null, CONDITION_ATT_LINE1, c.line1);
659 out.attribute(null, CONDITION_ATT_LINE2, c.line2);
660 out.attribute(null, CONDITION_ATT_ICON, Integer.toString(c.icon));
661 out.attribute(null, CONDITION_ATT_STATE, Integer.toString(c.state));
662 out.attribute(null, CONDITION_ATT_FLAGS, Integer.toString(c.flags));
663 }
664
John Spurlock056c5192014-04-20 21:52:01 -0400665 public static boolean isValidHour(int val) {
666 return val >= 0 && val < 24;
667 }
668
669 public static boolean isValidMinute(int val) {
670 return val >= 0 && val < 60;
671 }
672
John Spurlocka492d1d2015-05-05 18:30:28 -0400673 private static boolean isValidSource(int source) {
674 return source >= SOURCE_ANYONE && source <= MAX_SOURCE;
675 }
676
Julia Reynolds1f580572018-04-27 14:48:36 -0400677 private static Boolean unsafeBoolean(XmlPullParser parser, String att) {
678 final String val = parser.getAttributeValue(null, att);
679 if (TextUtils.isEmpty(val)) return null;
680 return Boolean.parseBoolean(val);
681 }
682
John Spurlock056c5192014-04-20 21:52:01 -0400683 private static boolean safeBoolean(XmlPullParser parser, String att, boolean defValue) {
684 final String val = parser.getAttributeValue(null, att);
Julia Reynolds1998ee52016-02-11 13:49:08 -0500685 return safeBoolean(val, defValue);
686 }
687
688 private static boolean safeBoolean(String val, boolean defValue) {
John Spurlock056c5192014-04-20 21:52:01 -0400689 if (TextUtils.isEmpty(val)) return defValue;
Tobias Thiererb0800dc2016-04-21 17:51:41 +0100690 return Boolean.parseBoolean(val);
John Spurlock056c5192014-04-20 21:52:01 -0400691 }
692
693 private static int safeInt(XmlPullParser parser, String att, int defValue) {
694 final String val = parser.getAttributeValue(null, att);
John Spurlockae641c92014-06-30 18:11:40 -0400695 return tryParseInt(val, defValue);
John Spurlock056c5192014-04-20 21:52:01 -0400696 }
697
John Spurlock3b98b3f2014-05-01 09:08:48 -0400698 private static ComponentName safeComponentName(XmlPullParser parser, String att) {
699 final String val = parser.getAttributeValue(null, att);
700 if (TextUtils.isEmpty(val)) return null;
701 return ComponentName.unflattenFromString(val);
702 }
703
704 private static Uri safeUri(XmlPullParser parser, String att) {
705 final String val = parser.getAttributeValue(null, att);
706 if (TextUtils.isEmpty(val)) return null;
707 return Uri.parse(val);
708 }
709
Julia Reynolds56106ff2015-09-30 14:42:53 -0400710 private static long safeLong(XmlPullParser parser, String att, long defValue) {
711 final String val = parser.getAttributeValue(null, att);
712 return tryParseLong(val, defValue);
713 }
714
John Spurlock056c5192014-04-20 21:52:01 -0400715 @Override
716 public int describeContents() {
717 return 0;
718 }
719
720 public ZenModeConfig copy() {
721 final Parcel parcel = Parcel.obtain();
722 try {
723 writeToParcel(parcel, 0);
724 parcel.setDataPosition(0);
725 return new ZenModeConfig(parcel);
726 } finally {
727 parcel.recycle();
728 }
729 }
730
731 public static final Parcelable.Creator<ZenModeConfig> CREATOR
732 = new Parcelable.Creator<ZenModeConfig>() {
733 @Override
734 public ZenModeConfig createFromParcel(Parcel source) {
735 return new ZenModeConfig(source);
736 }
737
738 @Override
739 public ZenModeConfig[] newArray(int size) {
740 return new ZenModeConfig[size];
741 }
742 };
John Spurlock856edeb2014-06-01 20:36:47 -0400743
John Spurlock1fc476d2015-04-14 16:05:20 -0400744 public Policy toNotificationPolicy() {
745 int priorityCategories = 0;
John Spurlocka492d1d2015-05-05 18:30:28 -0400746 int priorityCallSenders = Policy.PRIORITY_SENDERS_CONTACTS;
747 int priorityMessageSenders = Policy.PRIORITY_SENDERS_CONTACTS;
John Spurlock1fc476d2015-04-14 16:05:20 -0400748 if (allowCalls) {
749 priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS;
750 }
751 if (allowMessages) {
752 priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES;
753 }
754 if (allowEvents) {
755 priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS;
756 }
757 if (allowReminders) {
758 priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS;
759 }
760 if (allowRepeatCallers) {
761 priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS;
762 }
Beverly04216872017-09-28 10:55:32 -0400763 if (allowAlarms) {
764 priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS;
765 }
Beverlyd6964762018-02-16 14:07:03 -0500766 if (allowMedia) {
767 priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA;
768 }
769 if (allowSystem) {
770 priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM;
Beverly04216872017-09-28 10:55:32 -0400771 }
John Spurlocka492d1d2015-05-05 18:30:28 -0400772 priorityCallSenders = sourceToPrioritySenders(allowCallsFrom, priorityCallSenders);
773 priorityMessageSenders = sourceToPrioritySenders(allowMessagesFrom, priorityMessageSenders);
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500774 return new Policy(priorityCategories, priorityCallSenders, priorityMessageSenders,
Beverly86d076f2018-04-17 14:44:52 -0400775 suppressedVisualEffects, areChannelsBypassingDnd
776 ? Policy.STATE_CHANNELS_BYPASSING_DND : 0);
John Spurlocka492d1d2015-05-05 18:30:28 -0400777 }
778
Beverlybe6d3522017-11-20 11:01:59 -0500779 /**
780 * Creates scheduleCalendar from a condition id
781 * @param conditionId
782 * @return ScheduleCalendar with info populated with conditionId
783 */
784 public static ScheduleCalendar toScheduleCalendar(Uri conditionId) {
785 final ScheduleInfo schedule = ZenModeConfig.tryParseScheduleConditionId(conditionId);
786 if (schedule == null || schedule.days == null || schedule.days.length == 0) return null;
787 final ScheduleCalendar sc = new ScheduleCalendar();
788 sc.setSchedule(schedule);
789 sc.setTimeZone(TimeZone.getDefault());
790 return sc;
791 }
792
John Spurlocka492d1d2015-05-05 18:30:28 -0400793 private static int sourceToPrioritySenders(int source, int def) {
794 switch (source) {
795 case SOURCE_ANYONE: return Policy.PRIORITY_SENDERS_ANY;
796 case SOURCE_CONTACT: return Policy.PRIORITY_SENDERS_CONTACTS;
797 case SOURCE_STAR: return Policy.PRIORITY_SENDERS_STARRED;
798 default: return def;
John Spurlock1fc476d2015-04-14 16:05:20 -0400799 }
John Spurlocka492d1d2015-05-05 18:30:28 -0400800 }
801
802 private static int prioritySendersToSource(int prioritySenders, int def) {
803 switch (prioritySenders) {
804 case Policy.PRIORITY_SENDERS_CONTACTS: return SOURCE_CONTACT;
805 case Policy.PRIORITY_SENDERS_STARRED: return SOURCE_STAR;
806 case Policy.PRIORITY_SENDERS_ANY: return SOURCE_ANYONE;
807 default: return def;
808 }
John Spurlock1fc476d2015-04-14 16:05:20 -0400809 }
810
811 public void applyNotificationPolicy(Policy policy) {
812 if (policy == null) return;
Beverly04216872017-09-28 10:55:32 -0400813 allowAlarms = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS) != 0;
Beverlyd6964762018-02-16 14:07:03 -0500814 allowMedia = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MEDIA) != 0;
815 allowSystem = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_SYSTEM) != 0;
John Spurlock1fc476d2015-04-14 16:05:20 -0400816 allowEvents = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_EVENTS) != 0;
817 allowReminders = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REMINDERS) != 0;
Beverly04216872017-09-28 10:55:32 -0400818 allowCalls = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_CALLS) != 0;
819 allowMessages = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MESSAGES) != 0;
John Spurlock1fc476d2015-04-14 16:05:20 -0400820 allowRepeatCallers = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REPEAT_CALLERS)
821 != 0;
John Spurlock80774932015-05-07 17:38:50 -0400822 allowCallsFrom = prioritySendersToSource(policy.priorityCallSenders, allowCallsFrom);
823 allowMessagesFrom = prioritySendersToSource(policy.priorityMessageSenders,
824 allowMessagesFrom);
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500825 if (policy.suppressedVisualEffects != Policy.SUPPRESSED_EFFECTS_UNSET) {
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500826 suppressedVisualEffects = policy.suppressedVisualEffects;
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500827 }
Beverly86d076f2018-04-17 14:44:52 -0400828 if (policy.state != Policy.STATE_UNSET) {
829 areChannelsBypassingDnd = (policy.state & Policy.STATE_CHANNELS_BYPASSING_DND) != 0;
830 }
John Spurlock1fc476d2015-04-14 16:05:20 -0400831 }
832
Selim Cinek9c4a7072014-11-21 17:44:34 +0100833 public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle) {
John Spurlockbbde2672015-05-13 15:42:04 -0400834 return toTimeCondition(context, minutesFromNow, userHandle, false /*shortVersion*/);
835 }
836
837 public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle,
838 boolean shortVersion) {
Jason Monk4dd81462014-09-08 10:13:59 -0400839 final long now = System.currentTimeMillis();
840 final long millis = minutesFromNow == 0 ? ZERO_VALUE_MS : minutesFromNow * MINUTES_MS;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400841 return toTimeCondition(context, now + millis, minutesFromNow, userHandle, shortVersion);
Jason Monk4dd81462014-09-08 10:13:59 -0400842 }
843
Julia Reynolds0842fe82015-10-01 14:34:22 -0400844 public static Condition toTimeCondition(Context context, long time, int minutes,
John Spurlockbbde2672015-05-13 15:42:04 -0400845 int userHandle, boolean shortVersion) {
Julia Reynolds0842fe82015-10-01 14:34:22 -0400846 final int num;
847 String summary, line1, line2;
Julia Reynolds7635ab02017-07-25 08:34:44 -0400848 final CharSequence formattedTime =
849 getFormattedTime(context, time, isToday(time), userHandle);
Julia Reynolds0842fe82015-10-01 14:34:22 -0400850 final Resources res = context.getResources();
John Spurlockc90e6fe2014-10-28 11:21:42 -0400851 if (minutes < 60) {
852 // display as minutes
853 num = minutes;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400854 int summaryResId = shortVersion ? R.plurals.zen_mode_duration_minutes_summary_short
John Spurlockbbde2672015-05-13 15:42:04 -0400855 : R.plurals.zen_mode_duration_minutes_summary;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400856 summary = res.getQuantityString(summaryResId, num, num, formattedTime);
857 int line1ResId = shortVersion ? R.plurals.zen_mode_duration_minutes_short
John Spurlockbbde2672015-05-13 15:42:04 -0400858 : R.plurals.zen_mode_duration_minutes;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400859 line1 = res.getQuantityString(line1ResId, num, num, formattedTime);
860 line2 = res.getString(R.string.zen_mode_until, formattedTime);
861 } else if (minutes < DAY_MINUTES) {
John Spurlockc90e6fe2014-10-28 11:21:42 -0400862 // display as hours
863 num = Math.round(minutes / 60f);
Julia Reynolds0842fe82015-10-01 14:34:22 -0400864 int summaryResId = shortVersion ? R.plurals.zen_mode_duration_hours_summary_short
John Spurlockbbde2672015-05-13 15:42:04 -0400865 : R.plurals.zen_mode_duration_hours_summary;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400866 summary = res.getQuantityString(summaryResId, num, num, formattedTime);
867 int line1ResId = shortVersion ? R.plurals.zen_mode_duration_hours_short
John Spurlockbbde2672015-05-13 15:42:04 -0400868 : R.plurals.zen_mode_duration_hours;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400869 line1 = res.getQuantityString(line1ResId, num, num, formattedTime);
870 line2 = res.getString(R.string.zen_mode_until, formattedTime);
871 } else {
872 // display as day/time
873 summary = line1 = line2 = res.getString(R.string.zen_mode_until, formattedTime);
John Spurlockc90e6fe2014-10-28 11:21:42 -0400874 }
Julia Reynolds7635ab02017-07-25 08:34:44 -0400875 final Uri id = toCountdownConditionId(time, false);
John Spurlockc90e6fe2014-10-28 11:21:42 -0400876 return new Condition(id, summary, line1, line2, 0, Condition.STATE_TRUE,
Jason Monk4dd81462014-09-08 10:13:59 -0400877 Condition.FLAG_RELEVANT_NOW);
878 }
879
Julia Reynolds7635ab02017-07-25 08:34:44 -0400880 /**
881 * Converts countdown to alarm parameters into a condition with user facing summary
882 */
883 public static Condition toNextAlarmCondition(Context context, long alarm,
Julia Reynolds0842fe82015-10-01 14:34:22 -0400884 int userHandle) {
Julia Reynolds7635ab02017-07-25 08:34:44 -0400885 boolean isSameDay = isToday(alarm);
886 final CharSequence formattedTime = getFormattedTime(context, alarm, isSameDay, userHandle);
Julia Reynolds0842fe82015-10-01 14:34:22 -0400887 final Resources res = context.getResources();
Julia Reynolds7635ab02017-07-25 08:34:44 -0400888 final String line1 = res.getString(R.string.zen_mode_until, formattedTime);
889 final Uri id = toCountdownConditionId(alarm, true);
Julia Reynolds0842fe82015-10-01 14:34:22 -0400890 return new Condition(id, "", line1, "", 0, Condition.STATE_TRUE,
891 Condition.FLAG_RELEVANT_NOW);
892 }
893
Beverlybe6d3522017-11-20 11:01:59 -0500894 /**
895 * Creates readable time from time in milliseconds
896 */
897 public static CharSequence getFormattedTime(Context context, long time, boolean isSameDay,
Julia Reynolds7635ab02017-07-25 08:34:44 -0400898 int userHandle) {
899 String skeleton = (!isSameDay ? "EEE " : "")
900 + (DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma");
901 final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
902 return DateFormat.format(pattern, time);
903 }
904
Beverlybe6d3522017-11-20 11:01:59 -0500905 /**
906 * Determines whether a time in milliseconds is today or not
907 */
908 public static boolean isToday(long time) {
Julia Reynolds8e2d2bb2015-10-01 14:34:22 -0400909 GregorianCalendar now = new GregorianCalendar();
910 GregorianCalendar endTime = new GregorianCalendar();
911 endTime.setTimeInMillis(time);
912 if (now.get(Calendar.YEAR) == endTime.get(Calendar.YEAR)
913 && now.get(Calendar.MONTH) == endTime.get(Calendar.MONTH)
914 && now.get(Calendar.DATE) == endTime.get(Calendar.DATE)) {
Julia Reynolds7635ab02017-07-25 08:34:44 -0400915 return true;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400916 }
Julia Reynolds7635ab02017-07-25 08:34:44 -0400917 return false;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400918 }
919
John Spurlockd60258f2015-04-30 09:30:52 -0400920 // ==== Built-in system conditions ====
921
John Spurlock37bc92c2014-11-03 11:01:51 -0500922 public static final String SYSTEM_AUTHORITY = "android";
John Spurlock4db0d982014-08-13 09:19:03 -0400923
John Spurlockd60258f2015-04-30 09:30:52 -0400924 // ==== Built-in system condition: countdown ====
925
John Spurlock530052a2014-11-30 16:26:19 -0500926 public static final String COUNTDOWN_PATH = "countdown";
John Spurlock856edeb2014-06-01 20:36:47 -0400927
Julia Reynolds7635ab02017-07-25 08:34:44 -0400928 public static final String IS_ALARM_PATH = "alarm";
929
930 /**
931 * Converts countdown condition parameters into a condition id.
932 */
933 public static Uri toCountdownConditionId(long time, boolean alarm) {
John Spurlock856edeb2014-06-01 20:36:47 -0400934 return new Uri.Builder().scheme(Condition.SCHEME)
John Spurlock4db0d982014-08-13 09:19:03 -0400935 .authority(SYSTEM_AUTHORITY)
John Spurlock856edeb2014-06-01 20:36:47 -0400936 .appendPath(COUNTDOWN_PATH)
937 .appendPath(Long.toString(time))
Julia Reynolds7635ab02017-07-25 08:34:44 -0400938 .appendPath(IS_ALARM_PATH)
939 .appendPath(Boolean.toString(alarm))
John Spurlock856edeb2014-06-01 20:36:47 -0400940 .build();
941 }
942
943 public static long tryParseCountdownConditionId(Uri conditionId) {
John Spurlock4db0d982014-08-13 09:19:03 -0400944 if (!Condition.isValidId(conditionId, SYSTEM_AUTHORITY)) return 0;
Julia Reynolds7635ab02017-07-25 08:34:44 -0400945 if (conditionId.getPathSegments().size() < 2
John Spurlock856edeb2014-06-01 20:36:47 -0400946 || !COUNTDOWN_PATH.equals(conditionId.getPathSegments().get(0))) return 0;
947 try {
948 return Long.parseLong(conditionId.getPathSegments().get(1));
949 } catch (RuntimeException e) {
950 Slog.w(TAG, "Error parsing countdown condition: " + conditionId, e);
951 return 0;
952 }
953 }
954
Julia Reynolds7635ab02017-07-25 08:34:44 -0400955 /**
956 * Returns whether this condition is a countdown condition.
957 */
John Spurlock856edeb2014-06-01 20:36:47 -0400958 public static boolean isValidCountdownConditionId(Uri conditionId) {
959 return tryParseCountdownConditionId(conditionId) != 0;
960 }
John Spurlock4db0d982014-08-13 09:19:03 -0400961
Julia Reynolds7635ab02017-07-25 08:34:44 -0400962 /**
963 * Returns whether this condition is a countdown to an alarm.
964 */
965 public static boolean isValidCountdownToAlarmConditionId(Uri conditionId) {
966 if (tryParseCountdownConditionId(conditionId) != 0) {
967 if (conditionId.getPathSegments().size() < 4
968 || !IS_ALARM_PATH.equals(conditionId.getPathSegments().get(2))) {
969 return false;
970 }
971 try {
972 return Boolean.parseBoolean(conditionId.getPathSegments().get(3));
973 } catch (RuntimeException e) {
974 Slog.w(TAG, "Error parsing countdown alarm condition: " + conditionId, e);
975 return false;
976 }
977 }
978 return false;
979 }
980
John Spurlockd60258f2015-04-30 09:30:52 -0400981 // ==== Built-in system condition: schedule ====
982
John Spurlockb2278d62015-04-07 12:47:12 -0400983 public static final String SCHEDULE_PATH = "schedule";
John Spurlock4db0d982014-08-13 09:19:03 -0400984
John Spurlockd60258f2015-04-30 09:30:52 -0400985 public static Uri toScheduleConditionId(ScheduleInfo schedule) {
986 return new Uri.Builder().scheme(Condition.SCHEME)
987 .authority(SYSTEM_AUTHORITY)
988 .appendPath(SCHEDULE_PATH)
989 .appendQueryParameter("days", toDayList(schedule.days))
990 .appendQueryParameter("start", schedule.startHour + "." + schedule.startMinute)
991 .appendQueryParameter("end", schedule.endHour + "." + schedule.endMinute)
Julia Reynolds1998ee52016-02-11 13:49:08 -0500992 .appendQueryParameter("exitAtAlarm", String.valueOf(schedule.exitAtAlarm))
John Spurlockd60258f2015-04-30 09:30:52 -0400993 .build();
994 }
995
996 public static boolean isValidScheduleConditionId(Uri conditionId) {
Julia Reynolds4b318432017-11-22 10:56:18 -0500997 ScheduleInfo info;
998 try {
999 info = tryParseScheduleConditionId(conditionId);
1000 } catch (NullPointerException | ArrayIndexOutOfBoundsException e) {
1001 return false;
1002 }
1003
1004 if (info == null || info.days == null || info.days.length == 0) {
1005 return false;
1006 }
1007 return true;
John Spurlockd60258f2015-04-30 09:30:52 -04001008 }
1009
1010 public static ScheduleInfo tryParseScheduleConditionId(Uri conditionId) {
1011 final boolean isSchedule = conditionId != null
1012 && conditionId.getScheme().equals(Condition.SCHEME)
1013 && conditionId.getAuthority().equals(ZenModeConfig.SYSTEM_AUTHORITY)
1014 && conditionId.getPathSegments().size() == 1
1015 && conditionId.getPathSegments().get(0).equals(ZenModeConfig.SCHEDULE_PATH);
1016 if (!isSchedule) return null;
1017 final int[] start = tryParseHourAndMinute(conditionId.getQueryParameter("start"));
1018 final int[] end = tryParseHourAndMinute(conditionId.getQueryParameter("end"));
1019 if (start == null || end == null) return null;
1020 final ScheduleInfo rt = new ScheduleInfo();
1021 rt.days = tryParseDayList(conditionId.getQueryParameter("days"), "\\.");
1022 rt.startHour = start[0];
1023 rt.startMinute = start[1];
1024 rt.endHour = end[0];
1025 rt.endMinute = end[1];
Julia Reynolds1998ee52016-02-11 13:49:08 -05001026 rt.exitAtAlarm = safeBoolean(conditionId.getQueryParameter("exitAtAlarm"), false);
John Spurlockd60258f2015-04-30 09:30:52 -04001027 return rt;
1028 }
1029
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001030 public static ComponentName getScheduleConditionProvider() {
1031 return new ComponentName(SYSTEM_AUTHORITY, "ScheduleConditionProvider");
1032 }
1033
John Spurlockb2278d62015-04-07 12:47:12 -04001034 public static class ScheduleInfo {
1035 public int[] days;
1036 public int startHour;
1037 public int startMinute;
1038 public int endHour;
1039 public int endMinute;
Julia Reynolds1998ee52016-02-11 13:49:08 -05001040 public boolean exitAtAlarm;
1041 public long nextAlarm;
John Spurlockb2278d62015-04-07 12:47:12 -04001042
1043 @Override
1044 public int hashCode() {
1045 return 0;
1046 }
1047
1048 @Override
1049 public boolean equals(Object o) {
1050 if (!(o instanceof ScheduleInfo)) return false;
1051 final ScheduleInfo other = (ScheduleInfo) o;
1052 return toDayList(days).equals(toDayList(other.days))
1053 && startHour == other.startHour
1054 && startMinute == other.startMinute
1055 && endHour == other.endHour
Julia Reynolds1998ee52016-02-11 13:49:08 -05001056 && endMinute == other.endMinute
1057 && exitAtAlarm == other.exitAtAlarm;
John Spurlockb2278d62015-04-07 12:47:12 -04001058 }
1059
1060 public ScheduleInfo copy() {
1061 final ScheduleInfo rt = new ScheduleInfo();
1062 if (days != null) {
1063 rt.days = new int[days.length];
1064 System.arraycopy(days, 0, rt.days, 0, days.length);
1065 }
1066 rt.startHour = startHour;
1067 rt.startMinute = startMinute;
1068 rt.endHour = endHour;
1069 rt.endMinute = endMinute;
Julia Reynolds1998ee52016-02-11 13:49:08 -05001070 rt.exitAtAlarm = exitAtAlarm;
1071 rt.nextAlarm = nextAlarm;
John Spurlockb2278d62015-04-07 12:47:12 -04001072 return rt;
1073 }
Julia Reynolds7f733082016-04-26 14:19:19 -04001074
1075 @Override
1076 public String toString() {
1077 return "ScheduleInfo{" +
1078 "days=" + Arrays.toString(days) +
1079 ", startHour=" + startHour +
1080 ", startMinute=" + startMinute +
1081 ", endHour=" + endHour +
1082 ", endMinute=" + endMinute +
1083 ", exitAtAlarm=" + exitAtAlarm +
Julia Reynoldsfe58f1f2016-07-19 10:18:32 -04001084 ", nextAlarm=" + ts(nextAlarm) +
Julia Reynolds7f733082016-04-26 14:19:19 -04001085 '}';
1086 }
Julia Reynoldsfe58f1f2016-07-19 10:18:32 -04001087
1088 protected static String ts(long time) {
1089 return new Date(time) + " (" + time + ")";
1090 }
John Spurlockb2278d62015-04-07 12:47:12 -04001091 }
1092
John Spurlockd60258f2015-04-30 09:30:52 -04001093 // ==== Built-in system condition: event ====
1094
1095 public static final String EVENT_PATH = "event";
1096
1097 public static Uri toEventConditionId(EventInfo event) {
John Spurlock4db0d982014-08-13 09:19:03 -04001098 return new Uri.Builder().scheme(Condition.SCHEME)
1099 .authority(SYSTEM_AUTHORITY)
John Spurlockd60258f2015-04-30 09:30:52 -04001100 .appendPath(EVENT_PATH)
John Spurlock1b8b22b2015-05-20 09:47:13 -04001101 .appendQueryParameter("userId", Long.toString(event.userId))
John Spurlock995a7492015-05-28 22:13:03 -04001102 .appendQueryParameter("calendar", event.calendar != null ? event.calendar : "")
John Spurlockd60258f2015-04-30 09:30:52 -04001103 .appendQueryParameter("reply", Integer.toString(event.reply))
John Spurlock4db0d982014-08-13 09:19:03 -04001104 .build();
1105 }
1106
John Spurlockd60258f2015-04-30 09:30:52 -04001107 public static boolean isValidEventConditionId(Uri conditionId) {
1108 return tryParseEventConditionId(conditionId) != null;
John Spurlockb2278d62015-04-07 12:47:12 -04001109 }
1110
John Spurlockd60258f2015-04-30 09:30:52 -04001111 public static EventInfo tryParseEventConditionId(Uri conditionId) {
1112 final boolean isEvent = conditionId != null
John Spurlockb2278d62015-04-07 12:47:12 -04001113 && conditionId.getScheme().equals(Condition.SCHEME)
1114 && conditionId.getAuthority().equals(ZenModeConfig.SYSTEM_AUTHORITY)
1115 && conditionId.getPathSegments().size() == 1
John Spurlockd60258f2015-04-30 09:30:52 -04001116 && conditionId.getPathSegments().get(0).equals(EVENT_PATH);
1117 if (!isEvent) return null;
1118 final EventInfo rt = new EventInfo();
John Spurlock1b8b22b2015-05-20 09:47:13 -04001119 rt.userId = tryParseInt(conditionId.getQueryParameter("userId"), UserHandle.USER_NULL);
John Spurlock995a7492015-05-28 22:13:03 -04001120 rt.calendar = conditionId.getQueryParameter("calendar");
1121 if (TextUtils.isEmpty(rt.calendar) || tryParseLong(rt.calendar, -1L) != -1L) {
1122 rt.calendar = null;
1123 }
John Spurlockd60258f2015-04-30 09:30:52 -04001124 rt.reply = tryParseInt(conditionId.getQueryParameter("reply"), 0);
John Spurlockb2278d62015-04-07 12:47:12 -04001125 return rt;
John Spurlock4db0d982014-08-13 09:19:03 -04001126 }
1127
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001128 public static ComponentName getEventConditionProvider() {
1129 return new ComponentName(SYSTEM_AUTHORITY, "EventConditionProvider");
1130 }
1131
John Spurlockd60258f2015-04-30 09:30:52 -04001132 public static class EventInfo {
John Spurlockd39af2d2015-05-05 09:49:32 -04001133 public static final int REPLY_ANY_EXCEPT_NO = 0;
1134 public static final int REPLY_YES_OR_MAYBE = 1;
John Spurlockd60258f2015-04-30 09:30:52 -04001135 public static final int REPLY_YES = 2;
1136
John Spurlock1b8b22b2015-05-20 09:47:13 -04001137 public int userId = UserHandle.USER_NULL; // USER_NULL = unspecified - use current user
John Spurlock995a7492015-05-28 22:13:03 -04001138 public String calendar; // CalendarContract.Calendars.OWNER_ACCOUNT, or null for any
John Spurlockd60258f2015-04-30 09:30:52 -04001139 public int reply;
1140
1141 @Override
1142 public int hashCode() {
1143 return 0;
1144 }
1145
1146 @Override
1147 public boolean equals(Object o) {
1148 if (!(o instanceof EventInfo)) return false;
1149 final EventInfo other = (EventInfo) o;
John Spurlock1b8b22b2015-05-20 09:47:13 -04001150 return userId == other.userId
John Spurlock995a7492015-05-28 22:13:03 -04001151 && Objects.equals(calendar, other.calendar)
John Spurlockd60258f2015-04-30 09:30:52 -04001152 && reply == other.reply;
1153 }
1154
1155 public EventInfo copy() {
1156 final EventInfo rt = new EventInfo();
John Spurlock1b8b22b2015-05-20 09:47:13 -04001157 rt.userId = userId;
John Spurlockd60258f2015-04-30 09:30:52 -04001158 rt.calendar = calendar;
John Spurlockd60258f2015-04-30 09:30:52 -04001159 rt.reply = reply;
1160 return rt;
1161 }
John Spurlock1b8b22b2015-05-20 09:47:13 -04001162
1163 public static int resolveUserId(int userId) {
1164 return userId == UserHandle.USER_NULL ? ActivityManager.getCurrentUser() : userId;
John Spurlock1b8b22b2015-05-20 09:47:13 -04001165 }
John Spurlockd60258f2015-04-30 09:30:52 -04001166 }
1167
1168 // ==== End built-in system conditions ====
1169
John Spurlock4db0d982014-08-13 09:19:03 -04001170 private static int[] tryParseHourAndMinute(String value) {
1171 if (TextUtils.isEmpty(value)) return null;
1172 final int i = value.indexOf('.');
1173 if (i < 1 || i >= value.length() - 1) return null;
1174 final int hour = tryParseInt(value.substring(0, i), -1);
1175 final int minute = tryParseInt(value.substring(i + 1), -1);
1176 return isValidHour(hour) && isValidMinute(minute) ? new int[] { hour, minute } : null;
1177 }
1178
John Spurlockb2278d62015-04-07 12:47:12 -04001179 private static int tryParseZenMode(String value, int defValue) {
1180 final int rt = tryParseInt(value, defValue);
1181 return Global.isValidZenMode(rt) ? rt : defValue;
John Spurlock4db0d982014-08-13 09:19:03 -04001182 }
1183
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001184 public static String newRuleId() {
John Spurlockb2278d62015-04-07 12:47:12 -04001185 return UUID.randomUUID().toString().replace("-", "");
1186 }
1187
Beverlybe6d3522017-11-20 11:01:59 -05001188 /**
1189 * Gets the name of the app associated with owner
1190 */
1191 public static String getOwnerCaption(Context context, String owner) {
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001192 final PackageManager pm = context.getPackageManager();
1193 try {
1194 final ApplicationInfo info = pm.getApplicationInfo(owner, 0);
1195 if (info != null) {
1196 final CharSequence seq = info.loadLabel(pm);
1197 if (seq != null) {
1198 final String str = seq.toString().trim();
1199 if (str.length() > 0) {
1200 return str;
1201 }
1202 }
1203 }
1204 } catch (Throwable e) {
1205 Slog.w(TAG, "Error loading owner caption", e);
1206 }
1207 return "";
1208 }
1209
John Spurlockb2278d62015-04-07 12:47:12 -04001210 public static String getConditionSummary(Context context, ZenModeConfig config,
John Spurlockbbde2672015-05-13 15:42:04 -04001211 int userHandle, boolean shortVersion) {
1212 return getConditionLine(context, config, userHandle, false /*useLine1*/, shortVersion);
John Spurlockb2278d62015-04-07 12:47:12 -04001213 }
1214
1215 private static String getConditionLine(Context context, ZenModeConfig config,
John Spurlockbbde2672015-05-13 15:42:04 -04001216 int userHandle, boolean useLine1, boolean shortVersion) {
John Spurlockb2278d62015-04-07 12:47:12 -04001217 if (config == null) return "";
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001218 String summary = "";
John Spurlockb2278d62015-04-07 12:47:12 -04001219 if (config.manualRule != null) {
1220 final Uri id = config.manualRule.conditionId;
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001221 if (config.manualRule.enabler != null) {
1222 summary = getOwnerCaption(context, config.manualRule.enabler);
1223 } else {
1224 if (id == null) {
1225 summary = context.getString(com.android.internal.R.string.zen_mode_forever);
1226 } else {
1227 final long time = tryParseCountdownConditionId(id);
1228 Condition c = config.manualRule.condition;
1229 if (time > 0) {
1230 final long now = System.currentTimeMillis();
1231 final long span = time - now;
1232 c = toTimeCondition(context, time, Math.round(span / (float) MINUTES_MS),
1233 userHandle, shortVersion);
1234 }
1235 final String rt = c == null ? "" : useLine1 ? c.line1 : c.summary;
1236 summary = TextUtils.isEmpty(rt) ? "" : rt;
1237 }
John Spurlockb2278d62015-04-07 12:47:12 -04001238 }
John Spurlockb2278d62015-04-07 12:47:12 -04001239 }
John Spurlockb2278d62015-04-07 12:47:12 -04001240 for (ZenRule automaticRule : config.automaticRules.values()) {
John Spurlock39581cc2015-04-10 11:59:01 -04001241 if (automaticRule.isAutomaticActive()) {
John Spurlockb2278d62015-04-07 12:47:12 -04001242 if (summary.isEmpty()) {
1243 summary = automaticRule.name;
1244 } else {
1245 summary = context.getResources()
1246 .getString(R.string.zen_mode_rule_name_combination, summary,
1247 automaticRule.name);
1248 }
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001249
John Spurlockb2278d62015-04-07 12:47:12 -04001250 }
1251 }
1252 return summary;
1253 }
1254
1255 public static class ZenRule implements Parcelable {
1256 public boolean enabled;
1257 public boolean snoozing; // user manually disabled this instance
Julia Reynolds361e82d32016-02-26 18:19:49 -05001258 public String name; // required for automatic
John Spurlockb2278d62015-04-07 12:47:12 -04001259 public int zenMode;
1260 public Uri conditionId; // required for automatic
1261 public Condition condition; // optional
1262 public ComponentName component; // optional
Julia Reynolds56106ff2015-09-30 14:42:53 -04001263 public String id; // required for automatic (unique)
1264 public long creationTime; // required for automatic
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001265 public String enabler; // package name, only used for manual rules.
John Spurlockb2278d62015-04-07 12:47:12 -04001266
1267 public ZenRule() { }
1268
1269 public ZenRule(Parcel source) {
1270 enabled = source.readInt() == 1;
1271 snoozing = source.readInt() == 1;
1272 if (source.readInt() == 1) {
1273 name = source.readString();
1274 }
1275 zenMode = source.readInt();
1276 conditionId = source.readParcelable(null);
1277 condition = source.readParcelable(null);
1278 component = source.readParcelable(null);
Julia Reynolds56106ff2015-09-30 14:42:53 -04001279 if (source.readInt() == 1) {
1280 id = source.readString();
1281 }
1282 creationTime = source.readLong();
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001283 if (source.readInt() == 1) {
1284 enabler = source.readString();
1285 }
John Spurlockb2278d62015-04-07 12:47:12 -04001286 }
John Spurlock4db0d982014-08-13 09:19:03 -04001287
1288 @Override
John Spurlockb2278d62015-04-07 12:47:12 -04001289 public int describeContents() {
John Spurlock4db0d982014-08-13 09:19:03 -04001290 return 0;
1291 }
1292
1293 @Override
John Spurlockb2278d62015-04-07 12:47:12 -04001294 public void writeToParcel(Parcel dest, int flags) {
1295 dest.writeInt(enabled ? 1 : 0);
1296 dest.writeInt(snoozing ? 1 : 0);
1297 if (name != null) {
1298 dest.writeInt(1);
1299 dest.writeString(name);
1300 } else {
1301 dest.writeInt(0);
1302 }
1303 dest.writeInt(zenMode);
1304 dest.writeParcelable(conditionId, 0);
1305 dest.writeParcelable(condition, 0);
1306 dest.writeParcelable(component, 0);
Julia Reynolds56106ff2015-09-30 14:42:53 -04001307 if (id != null) {
1308 dest.writeInt(1);
1309 dest.writeString(id);
1310 } else {
1311 dest.writeInt(0);
1312 }
1313 dest.writeLong(creationTime);
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001314 if (enabler != null) {
1315 dest.writeInt(1);
1316 dest.writeString(enabler);
1317 } else {
1318 dest.writeInt(0);
1319 }
John Spurlockb2278d62015-04-07 12:47:12 -04001320 }
1321
1322 @Override
1323 public String toString() {
1324 return new StringBuilder(ZenRule.class.getSimpleName()).append('[')
1325 .append("enabled=").append(enabled)
1326 .append(",snoozing=").append(snoozing)
1327 .append(",name=").append(name)
1328 .append(",zenMode=").append(Global.zenModeToString(zenMode))
1329 .append(",conditionId=").append(conditionId)
1330 .append(",condition=").append(condition)
1331 .append(",component=").append(component)
Julia Reynolds56106ff2015-09-30 14:42:53 -04001332 .append(",id=").append(id)
1333 .append(",creationTime=").append(creationTime)
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001334 .append(",enabler=").append(enabler)
John Spurlockb2278d62015-04-07 12:47:12 -04001335 .append(']').toString();
1336 }
1337
Kweku Adams99546332018-01-24 17:03:50 -08001338 /** @hide */
1339 public void writeToProto(ProtoOutputStream proto, long fieldId) {
1340 final long token = proto.start(fieldId);
1341
1342 proto.write(ZenRuleProto.ID, id);
1343 proto.write(ZenRuleProto.NAME, name);
1344 proto.write(ZenRuleProto.CREATION_TIME_MS, creationTime);
1345 proto.write(ZenRuleProto.ENABLED, enabled);
1346 proto.write(ZenRuleProto.ENABLER, enabler);
1347 proto.write(ZenRuleProto.IS_SNOOZING, snoozing);
1348 proto.write(ZenRuleProto.ZEN_MODE, zenMode);
1349 if (conditionId != null) {
1350 proto.write(ZenRuleProto.CONDITION_ID, conditionId.toString());
1351 }
1352 if (condition != null) {
1353 condition.writeToProto(proto, ZenRuleProto.CONDITION);
1354 }
1355 if (component != null) {
1356 component.writeToProto(proto, ZenRuleProto.COMPONENT);
1357 }
1358
1359 proto.end(token);
1360 }
1361
John Spurlock21258a32015-05-27 18:22:55 -04001362 private static void appendDiff(Diff d, String item, ZenRule from, ZenRule to) {
1363 if (d == null) return;
1364 if (from == null) {
1365 if (to != null) {
1366 d.addLine(item, "insert");
1367 }
1368 return;
1369 }
1370 from.appendDiff(d, item, to);
1371 }
1372
1373 private void appendDiff(Diff d, String item, ZenRule to) {
1374 if (to == null) {
1375 d.addLine(item, "delete");
1376 return;
1377 }
1378 if (enabled != to.enabled) {
1379 d.addLine(item, "enabled", enabled, to.enabled);
1380 }
1381 if (snoozing != to.snoozing) {
1382 d.addLine(item, "snoozing", snoozing, to.snoozing);
1383 }
1384 if (!Objects.equals(name, to.name)) {
1385 d.addLine(item, "name", name, to.name);
1386 }
1387 if (zenMode != to.zenMode) {
1388 d.addLine(item, "zenMode", zenMode, to.zenMode);
1389 }
1390 if (!Objects.equals(conditionId, to.conditionId)) {
1391 d.addLine(item, "conditionId", conditionId, to.conditionId);
1392 }
1393 if (!Objects.equals(condition, to.condition)) {
1394 d.addLine(item, "condition", condition, to.condition);
1395 }
1396 if (!Objects.equals(component, to.component)) {
1397 d.addLine(item, "component", component, to.component);
1398 }
Julia Reynolds56106ff2015-09-30 14:42:53 -04001399 if (!Objects.equals(id, to.id)) {
1400 d.addLine(item, "id", id, to.id);
1401 }
Julia Reynoldsc8e54e82015-11-30 16:43:05 -05001402 if (creationTime != to.creationTime) {
Julia Reynolds56106ff2015-09-30 14:42:53 -04001403 d.addLine(item, "creationTime", creationTime, to.creationTime);
1404 }
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001405 if (enabler != to.enabler) {
1406 d.addLine(item, "enabler", enabler, to.enabler);
1407 }
John Spurlock21258a32015-05-27 18:22:55 -04001408 }
1409
John Spurlockb2278d62015-04-07 12:47:12 -04001410 @Override
John Spurlock4db0d982014-08-13 09:19:03 -04001411 public boolean equals(Object o) {
John Spurlockb2278d62015-04-07 12:47:12 -04001412 if (!(o instanceof ZenRule)) return false;
1413 if (o == this) return true;
1414 final ZenRule other = (ZenRule) o;
1415 return other.enabled == enabled
1416 && other.snoozing == snoozing
1417 && Objects.equals(other.name, name)
1418 && other.zenMode == zenMode
1419 && Objects.equals(other.conditionId, conditionId)
1420 && Objects.equals(other.condition, condition)
Julia Reynolds56106ff2015-09-30 14:42:53 -04001421 && Objects.equals(other.component, component)
1422 && Objects.equals(other.id, id)
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001423 && other.creationTime == creationTime
1424 && Objects.equals(other.enabler, enabler);
John Spurlockb2278d62015-04-07 12:47:12 -04001425 }
1426
1427 @Override
1428 public int hashCode() {
1429 return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001430 component, id, creationTime, enabler);
John Spurlockb2278d62015-04-07 12:47:12 -04001431 }
1432
John Spurlock39581cc2015-04-10 11:59:01 -04001433 public boolean isAutomaticActive() {
1434 return enabled && !snoozing && component != null && isTrueOrUnknown();
1435 }
1436
John Spurlockb2278d62015-04-07 12:47:12 -04001437 public boolean isTrueOrUnknown() {
John Spurlock39581cc2015-04-10 11:59:01 -04001438 return condition != null && (condition.state == Condition.STATE_TRUE
1439 || condition.state == Condition.STATE_UNKNOWN);
John Spurlockb2278d62015-04-07 12:47:12 -04001440 }
1441
1442 public static final Parcelable.Creator<ZenRule> CREATOR
1443 = new Parcelable.Creator<ZenRule>() {
1444 @Override
1445 public ZenRule createFromParcel(Parcel source) {
1446 return new ZenRule(source);
1447 }
1448 @Override
1449 public ZenRule[] newArray(int size) {
1450 return new ZenRule[size];
1451 }
1452 };
1453 }
1454
John Spurlock21258a32015-05-27 18:22:55 -04001455 public static class Diff {
1456 private final ArrayList<String> lines = new ArrayList<>();
1457
1458 @Override
1459 public String toString() {
1460 final StringBuilder sb = new StringBuilder("Diff[");
1461 final int N = lines.size();
1462 for (int i = 0; i < N; i++) {
1463 if (i > 0) {
1464 sb.append(',');
1465 }
1466 sb.append(lines.get(i));
1467 }
1468 return sb.append(']').toString();
1469 }
1470
1471 private Diff addLine(String item, String action) {
1472 lines.add(item + ":" + action);
1473 return this;
1474 }
1475
1476 public Diff addLine(String item, String subitem, Object from, Object to) {
1477 return addLine(item + "." + subitem, from, to);
1478 }
1479
1480 public Diff addLine(String item, Object from, Object to) {
1481 return addLine(item, from + "->" + to);
1482 }
1483 }
1484
Beverly925cde82018-01-23 09:31:23 -05001485 /**
Beverlyd6964762018-02-16 14:07:03 -05001486 * Determines whether dnd behavior should mute all notification/ringer sounds
1487 * (sounds associated with ringer volume discluding system)
Beverly925cde82018-01-23 09:31:23 -05001488 */
1489 public static boolean areAllPriorityOnlyNotificationZenSoundsMuted(NotificationManager.Policy
1490 policy) {
1491 boolean allowReminders = (policy.priorityCategories
1492 & NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS) != 0;
1493 boolean allowCalls = (policy.priorityCategories
1494 & NotificationManager.Policy.PRIORITY_CATEGORY_CALLS) != 0;
1495 boolean allowMessages = (policy.priorityCategories
1496 & NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) != 0;
1497 boolean allowEvents = (policy.priorityCategories
1498 & NotificationManager.Policy.PRIORITY_CATEGORY_EVENTS) != 0;
1499 boolean allowRepeatCallers = (policy.priorityCategories
1500 & NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS) != 0;
Beverly86d076f2018-04-17 14:44:52 -04001501 boolean areChannelsBypassingDnd = (policy.state & Policy.STATE_CHANNELS_BYPASSING_DND) != 0;
Beverly925cde82018-01-23 09:31:23 -05001502 return !allowReminders && !allowCalls && !allowMessages && !allowEvents
Beverly86d076f2018-04-17 14:44:52 -04001503 && !allowRepeatCallers && !areChannelsBypassingDnd;
Beverly925cde82018-01-23 09:31:23 -05001504 }
1505
1506 /**
Beverlyb9826dd2018-04-12 15:02:45 -04001507 * Determines if DND is currently overriding the ringer
1508 */
1509 public static boolean isZenOverridingRinger(int zen, ZenModeConfig zenConfig) {
Beverlyb9826dd2018-04-12 15:02:45 -04001510 return zen == Global.ZEN_MODE_NO_INTERRUPTIONS
1511 || zen == Global.ZEN_MODE_ALARMS
1512 || (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
1513 && ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(zenConfig));
1514 }
1515
1516 /**
Beverlyd6964762018-02-16 14:07:03 -05001517 * Determines whether dnd behavior should mute all sounds controlled by ringer
Beverly925cde82018-01-23 09:31:23 -05001518 */
1519 public static boolean areAllPriorityOnlyNotificationZenSoundsMuted(ZenModeConfig config) {
1520 return !config.allowReminders && !config.allowCalls && !config.allowMessages
Beverly86d076f2018-04-17 14:44:52 -04001521 && !config.allowEvents && !config.allowRepeatCallers
1522 && !config.areChannelsBypassingDnd;
Beverly925cde82018-01-23 09:31:23 -05001523 }
1524
1525 /**
1526 * Determines whether all dnd mutes all sounds
1527 */
1528 public static boolean areAllZenBehaviorSoundsMuted(ZenModeConfig config) {
Beverlyd6964762018-02-16 14:07:03 -05001529 return !config.allowAlarms && !config.allowMedia && !config.allowSystem
Beverly925cde82018-01-23 09:31:23 -05001530 && areAllPriorityOnlyNotificationZenSoundsMuted(config);
1531 }
Beverlya5effb42018-02-01 17:08:35 -05001532
1533 /**
1534 * Returns a description of the current do not disturb settings from config.
1535 * - If turned on manually and end time is known, returns end time.
Beverlyebcaeeb2018-04-06 14:15:23 -04001536 * - If turned on manually and end time is on forever until turned off, return null if
1537 * describeForeverCondition is false, else return String describing indefinite behavior
Beverlya5effb42018-02-01 17:08:35 -05001538 * - If turned on by an automatic rule, returns the automatic rule name.
1539 * - If on due to an app, returns the app name.
1540 * - If there's a combination of rules/apps that trigger, then shows the one that will
1541 * last the longest if applicable.
Beverlyebcaeeb2018-04-06 14:15:23 -04001542 * @return null if DND is off or describeForeverCondition is false and
1543 * DND is on forever (until turned off)
Beverlya5effb42018-02-01 17:08:35 -05001544 */
Beverlyebcaeeb2018-04-06 14:15:23 -04001545 public static String getDescription(Context context, boolean zenOn, ZenModeConfig config,
1546 boolean describeForeverCondition) {
1547 if (!zenOn || config == null) {
Beverlya5effb42018-02-01 17:08:35 -05001548 return null;
1549 }
1550
1551 String secondaryText = "";
1552 long latestEndTime = -1;
1553
1554 // DND turned on by manual rule
1555 if (config.manualRule != null) {
1556 final Uri id = config.manualRule.conditionId;
1557 if (config.manualRule.enabler != null) {
1558 // app triggered manual rule
1559 String appName = getOwnerCaption(context, config.manualRule.enabler);
1560 if (!appName.isEmpty()) {
1561 secondaryText = appName;
1562 }
1563 } else {
1564 if (id == null) {
1565 // Do not disturb manually triggered to remain on forever until turned off
Beverlyebcaeeb2018-04-06 14:15:23 -04001566 if (describeForeverCondition) {
1567 return context.getString(R.string.zen_mode_forever);
1568 } else {
1569 return null;
1570 }
Beverlya5effb42018-02-01 17:08:35 -05001571 } else {
1572 latestEndTime = tryParseCountdownConditionId(id);
1573 if (latestEndTime > 0) {
1574 final CharSequence formattedTime = getFormattedTime(context,
1575 latestEndTime, isToday(latestEndTime),
1576 context.getUserId());
1577 secondaryText = context.getString(R.string.zen_mode_until, formattedTime);
1578 }
1579 }
1580 }
1581 }
1582
1583 // DND turned on by an automatic rule
1584 for (ZenRule automaticRule : config.automaticRules.values()) {
1585 if (automaticRule.isAutomaticActive()) {
1586 if (isValidEventConditionId(automaticRule.conditionId)
1587 || isValidScheduleConditionId(automaticRule.conditionId)) {
1588 // set text if automatic rule end time is the latest active rule end time
1589 long endTime = parseAutomaticRuleEndTime(context, automaticRule.conditionId);
1590 if (endTime > latestEndTime) {
1591 latestEndTime = endTime;
1592 secondaryText = automaticRule.name;
1593 }
1594 } else {
1595 // set text if 3rd party rule
1596 return automaticRule.name;
1597 }
1598 }
1599 }
1600
1601 return !secondaryText.equals("") ? secondaryText : null;
1602 }
1603
1604 private static long parseAutomaticRuleEndTime(Context context, Uri id) {
1605 if (isValidEventConditionId(id)) {
1606 // cannot look up end times for events
1607 return Long.MAX_VALUE;
1608 }
1609
1610 if (isValidScheduleConditionId(id)) {
1611 ScheduleCalendar schedule = toScheduleCalendar(id);
1612 long endTimeMs = schedule.getNextChangeTime(System.currentTimeMillis());
1613
1614 // check if automatic rule will end on next alarm
1615 if (schedule.exitAtAlarm()) {
1616 long nextAlarm = getNextAlarm(context);
1617 schedule.maybeSetNextAlarm(System.currentTimeMillis(), nextAlarm);
1618 if (schedule.shouldExitForAlarm(endTimeMs)) {
1619 return nextAlarm;
1620 }
1621 }
1622
1623 return endTimeMs;
1624 }
1625
1626 return -1;
1627 }
1628
1629 private static long getNextAlarm(Context context) {
1630 final AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
1631 final AlarmManager.AlarmClockInfo info = alarms.getNextAlarmClock(context.getUserId());
1632 return info != null ? info.getTriggerTime() : 0;
1633 }
John Spurlock056c5192014-04-20 21:52:01 -04001634}