blob: 5edf7ce476587e450e95b8d07d847fb2b0e4e392 [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 Reynolds4bcf9b82018-07-13 15:12:29 -0400243 .append(",\nautomaticRules=").append(rulesToString())
244 .append(",\nmanualRule=").append(manualRule)
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500245 .append(']').toString();
John Spurlock056c5192014-04-20 21:52:01 -0400246 }
247
Julia Reynolds4bcf9b82018-07-13 15:12:29 -0400248 private String rulesToString() {
249 if (automaticRules.isEmpty()) {
250 return "{}";
251 }
252
253 StringBuilder buffer = new StringBuilder(automaticRules.size() * 28);
254 buffer.append('{');
255 for (int i = 0; i < automaticRules.size(); i++) {
256 if (i > 0) {
257 buffer.append(",\n");
258 }
259 Object value = automaticRules.valueAt(i);
260 buffer.append(value);
261 }
262 buffer.append('}');
263 return buffer.toString();
264 }
265
John Spurlock21258a32015-05-27 18:22:55 -0400266 private Diff diff(ZenModeConfig to) {
267 final Diff d = new Diff();
268 if (to == null) {
269 return d.addLine("config", "delete");
270 }
271 if (user != to.user) {
272 d.addLine("user", user, to.user);
273 }
Beverly04216872017-09-28 10:55:32 -0400274 if (allowAlarms != to.allowAlarms) {
275 d.addLine("allowAlarms", allowAlarms, to.allowAlarms);
276 }
Beverlyd6964762018-02-16 14:07:03 -0500277 if (allowMedia != to.allowMedia) {
278 d.addLine("allowMedia", allowMedia, to.allowMedia);
279 }
280 if (allowSystem != to.allowSystem) {
281 d.addLine("allowSystem", allowSystem, to.allowSystem);
Beverly04216872017-09-28 10:55:32 -0400282 }
John Spurlock21258a32015-05-27 18:22:55 -0400283 if (allowCalls != to.allowCalls) {
284 d.addLine("allowCalls", allowCalls, to.allowCalls);
285 }
Beverly04216872017-09-28 10:55:32 -0400286 if (allowReminders != to.allowReminders) {
287 d.addLine("allowReminders", allowReminders, to.allowReminders);
288 }
289 if (allowEvents != to.allowEvents) {
290 d.addLine("allowEvents", allowEvents, to.allowEvents);
291 }
John Spurlock21258a32015-05-27 18:22:55 -0400292 if (allowRepeatCallers != to.allowRepeatCallers) {
293 d.addLine("allowRepeatCallers", allowRepeatCallers, to.allowRepeatCallers);
294 }
295 if (allowMessages != to.allowMessages) {
296 d.addLine("allowMessages", allowMessages, to.allowMessages);
297 }
298 if (allowCallsFrom != to.allowCallsFrom) {
299 d.addLine("allowCallsFrom", allowCallsFrom, to.allowCallsFrom);
300 }
301 if (allowMessagesFrom != to.allowMessagesFrom) {
302 d.addLine("allowMessagesFrom", allowMessagesFrom, to.allowMessagesFrom);
303 }
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500304 if (suppressedVisualEffects != to.suppressedVisualEffects) {
305 d.addLine("suppressedVisualEffects", suppressedVisualEffects,
306 to.suppressedVisualEffects);
307 }
John Spurlock21258a32015-05-27 18:22:55 -0400308 final ArraySet<String> allRules = new ArraySet<>();
309 addKeys(allRules, automaticRules);
310 addKeys(allRules, to.automaticRules);
311 final int N = allRules.size();
312 for (int i = 0; i < N; i++) {
313 final String rule = allRules.valueAt(i);
314 final ZenRule fromRule = automaticRules != null ? automaticRules.get(rule) : null;
315 final ZenRule toRule = to.automaticRules != null ? to.automaticRules.get(rule) : null;
316 ZenRule.appendDiff(d, "automaticRule[" + rule + "]", fromRule, toRule);
317 }
318 ZenRule.appendDiff(d, "manualRule", manualRule, to.manualRule);
Beverly86d076f2018-04-17 14:44:52 -0400319
320 if (areChannelsBypassingDnd != to.areChannelsBypassingDnd) {
321 d.addLine("areChannelsBypassingDnd", areChannelsBypassingDnd,
322 to.areChannelsBypassingDnd);
323 }
John Spurlock21258a32015-05-27 18:22:55 -0400324 return d;
325 }
326
327 public static Diff diff(ZenModeConfig from, ZenModeConfig to) {
328 if (from == null) {
329 final Diff d = new Diff();
330 if (to != null) {
331 d.addLine("config", "insert");
332 }
333 return d;
334 }
335 return from.diff(to);
336 }
337
338 private static <T> void addKeys(ArraySet<T> set, ArrayMap<T, ?> map) {
339 if (map != null) {
340 for (int i = 0; i < map.size(); i++) {
341 set.add(map.keyAt(i));
342 }
343 }
344 }
345
John Spurlockb2278d62015-04-07 12:47:12 -0400346 public boolean isValid() {
347 if (!isValidManualRule(manualRule)) return false;
348 final int N = automaticRules.size();
349 for (int i = 0; i < N; i++) {
350 if (!isValidAutomaticRule(automaticRules.valueAt(i))) return false;
351 }
352 return true;
353 }
354
355 private static boolean isValidManualRule(ZenRule rule) {
356 return rule == null || Global.isValidZenMode(rule.zenMode) && sameCondition(rule);
357 }
358
359 private static boolean isValidAutomaticRule(ZenRule rule) {
360 return rule != null && !TextUtils.isEmpty(rule.name) && Global.isValidZenMode(rule.zenMode)
361 && rule.conditionId != null && sameCondition(rule);
362 }
363
364 private static boolean sameCondition(ZenRule rule) {
365 if (rule == null) return false;
366 if (rule.conditionId == null) {
367 return rule.condition == null;
368 } else {
369 return rule.condition == null || rule.conditionId.equals(rule.condition.id);
370 }
371 }
372
John Spurlockd9c75db2015-04-28 11:19:13 -0400373 private static int[] generateMinuteBuckets() {
374 final int maxHrs = 12;
375 final int[] buckets = new int[maxHrs + 3];
376 buckets[0] = 15;
377 buckets[1] = 30;
378 buckets[2] = 45;
379 for (int i = 1; i <= maxHrs; i++) {
380 buckets[2 + i] = 60 * i;
381 }
382 return buckets;
383 }
384
Chris Wren99f963e2014-05-28 16:52:42 -0400385 public static String sourceToString(int source) {
386 switch (source) {
387 case SOURCE_ANYONE:
388 return "anyone";
389 case SOURCE_CONTACT:
390 return "contacts";
391 case SOURCE_STAR:
392 return "stars";
393 default:
394 return "UNKNOWN";
395 }
396 }
397
John Spurlock056c5192014-04-20 21:52:01 -0400398 @Override
399 public boolean equals(Object o) {
400 if (!(o instanceof ZenModeConfig)) return false;
401 if (o == this) return true;
402 final ZenModeConfig other = (ZenModeConfig) o;
Beverly04216872017-09-28 10:55:32 -0400403 return other.allowAlarms == allowAlarms
Beverlyd6964762018-02-16 14:07:03 -0500404 && other.allowMedia == allowMedia
405 && other.allowSystem == allowSystem
Beverly04216872017-09-28 10:55:32 -0400406 && other.allowCalls == allowCalls
John Spurlock1d7d2242015-04-10 08:10:22 -0400407 && other.allowRepeatCallers == allowRepeatCallers
John Spurlock056c5192014-04-20 21:52:01 -0400408 && other.allowMessages == allowMessages
John Spurlocka492d1d2015-05-05 18:30:28 -0400409 && other.allowCallsFrom == allowCallsFrom
410 && other.allowMessagesFrom == allowMessagesFrom
John Spurlockfc746f82015-04-03 13:47:14 -0400411 && other.allowReminders == allowReminders
John Spurlock2dac62c2014-09-19 15:29:06 -0400412 && other.allowEvents == allowEvents
John Spurlock21258a32015-05-27 18:22:55 -0400413 && other.user == user
John Spurlockb2278d62015-04-07 12:47:12 -0400414 && Objects.equals(other.automaticRules, automaticRules)
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500415 && Objects.equals(other.manualRule, manualRule)
Beverly86d076f2018-04-17 14:44:52 -0400416 && other.suppressedVisualEffects == suppressedVisualEffects
417 && other.areChannelsBypassingDnd == areChannelsBypassingDnd;
John Spurlock056c5192014-04-20 21:52:01 -0400418 }
419
420 @Override
421 public int hashCode() {
Beverlyd6964762018-02-16 14:07:03 -0500422 return Objects.hash(allowAlarms, allowMedia, allowSystem, allowCalls,
Beverly04216872017-09-28 10:55:32 -0400423 allowRepeatCallers, allowMessages,
424 allowCallsFrom, allowMessagesFrom, allowReminders, allowEvents,
Julia Reynolds1f580572018-04-27 14:48:36 -0400425 user, automaticRules, manualRule,
Beverly86d076f2018-04-17 14:44:52 -0400426 suppressedVisualEffects, areChannelsBypassingDnd);
John Spurlock056c5192014-04-20 21:52:01 -0400427 }
428
John Spurlockb2278d62015-04-07 12:47:12 -0400429 private static String toDayList(int[] days) {
430 if (days == null || days.length == 0) return "";
431 final StringBuilder sb = new StringBuilder();
432 for (int i = 0; i < days.length; i++) {
433 if (i > 0) sb.append('.');
434 sb.append(days[i]);
435 }
436 return sb.toString();
John Spurlockae641c92014-06-30 18:11:40 -0400437 }
438
John Spurlockb2278d62015-04-07 12:47:12 -0400439 private static int[] tryParseDayList(String dayList, String sep) {
440 if (dayList == null) return null;
441 final String[] tokens = dayList.split(sep);
John Spurlockae641c92014-06-30 18:11:40 -0400442 if (tokens.length == 0) return null;
443 final int[] rt = new int[tokens.length];
444 for (int i = 0; i < tokens.length; i++) {
445 final int day = tryParseInt(tokens[i], -1);
446 if (day == -1) return null;
447 rt[i] = day;
448 }
449 return rt;
450 }
451
452 private static int tryParseInt(String value, int defValue) {
453 if (TextUtils.isEmpty(value)) return defValue;
454 try {
Narayan Kamatha09b4d22016-04-15 18:32:45 +0100455 return Integer.parseInt(value);
John Spurlockae641c92014-06-30 18:11:40 -0400456 } catch (NumberFormatException e) {
457 return defValue;
458 }
John Spurlock056c5192014-04-20 21:52:01 -0400459 }
460
John Spurlockd60258f2015-04-30 09:30:52 -0400461 private static long tryParseLong(String value, long defValue) {
462 if (TextUtils.isEmpty(value)) return defValue;
463 try {
Tobias Thierer28532d02016-04-21 14:52:10 +0100464 return Long.parseLong(value);
John Spurlockd60258f2015-04-30 09:30:52 -0400465 } catch (NumberFormatException e) {
466 return defValue;
467 }
468 }
469
Julia Reynolds206c7e92016-09-15 10:38:03 -0400470 public static ZenModeConfig readXml(XmlPullParser parser)
John Spurlock056c5192014-04-20 21:52:01 -0400471 throws XmlPullParserException, IOException {
472 int type = parser.getEventType();
473 if (type != XmlPullParser.START_TAG) return null;
474 String tag = parser.getName();
475 if (!ZEN_TAG.equals(tag)) return null;
476 final ZenModeConfig rt = new ZenModeConfig();
Beverly3bae4e52018-02-07 12:32:02 -0500477 rt.version = safeInt(parser, ZEN_ATT_VERSION, XML_VERSION);
John Spurlock21258a32015-05-27 18:22:55 -0400478 rt.user = safeInt(parser, ZEN_ATT_USER, rt.user);
Julia Reynolds1f580572018-04-27 14:48:36 -0400479 boolean readSuppressedEffects = false;
John Spurlock056c5192014-04-20 21:52:01 -0400480 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
481 tag = parser.getName();
John Spurlock3b98b3f2014-05-01 09:08:48 -0400482 if (type == XmlPullParser.END_TAG && ZEN_TAG.equals(tag)) {
John Spurlock3b98b3f2014-05-01 09:08:48 -0400483 return rt;
484 }
John Spurlock056c5192014-04-20 21:52:01 -0400485 if (type == XmlPullParser.START_TAG) {
486 if (ALLOW_TAG.equals(tag)) {
Beverly04216872017-09-28 10:55:32 -0400487 rt.allowCalls = safeBoolean(parser, ALLOW_ATT_CALLS,
488 DEFAULT_ALLOW_CALLS);
John Spurlock1d7d2242015-04-10 08:10:22 -0400489 rt.allowRepeatCallers = safeBoolean(parser, ALLOW_ATT_REPEAT_CALLERS,
490 DEFAULT_ALLOW_REPEAT_CALLERS);
Beverly04216872017-09-28 10:55:32 -0400491 rt.allowMessages = safeBoolean(parser, ALLOW_ATT_MESSAGES,
492 DEFAULT_ALLOW_MESSAGES);
John Spurlockfc746f82015-04-03 13:47:14 -0400493 rt.allowReminders = safeBoolean(parser, ALLOW_ATT_REMINDERS,
494 DEFAULT_ALLOW_REMINDERS);
John Spurlock2dac62c2014-09-19 15:29:06 -0400495 rt.allowEvents = safeBoolean(parser, ALLOW_ATT_EVENTS, DEFAULT_ALLOW_EVENTS);
John Spurlocka492d1d2015-05-05 18:30:28 -0400496 final int from = safeInt(parser, ALLOW_ATT_FROM, -1);
497 final int callsFrom = safeInt(parser, ALLOW_ATT_CALLS_FROM, -1);
498 final int messagesFrom = safeInt(parser, ALLOW_ATT_MESSAGES_FROM, -1);
499 if (isValidSource(callsFrom) && isValidSource(messagesFrom)) {
500 rt.allowCallsFrom = callsFrom;
501 rt.allowMessagesFrom = messagesFrom;
502 } else if (isValidSource(from)) {
503 Slog.i(TAG, "Migrating existing shared 'from': " + sourceToString(from));
504 rt.allowCallsFrom = from;
505 rt.allowMessagesFrom = from;
506 } else {
Julia Reynolds10a7cc12018-05-03 10:56:24 -0400507 rt.allowCallsFrom = DEFAULT_CALLS_SOURCE;
John Spurlocka492d1d2015-05-05 18:30:28 -0400508 rt.allowMessagesFrom = DEFAULT_SOURCE;
Chris Wren99f963e2014-05-28 16:52:42 -0400509 }
Beverly04216872017-09-28 10:55:32 -0400510 rt.allowAlarms = safeBoolean(parser, ALLOW_ATT_ALARMS, DEFAULT_ALLOW_ALARMS);
Beverlyd6964762018-02-16 14:07:03 -0500511 rt.allowMedia = safeBoolean(parser, ALLOW_ATT_MEDIA,
512 DEFAULT_ALLOW_MEDIA);
513 rt.allowSystem = safeBoolean(parser, ALLOW_ATT_SYSTEM, DEFAULT_ALLOW_SYSTEM);
Julia Reynolds1f580572018-04-27 14:48:36 -0400514
515 // migrate old suppressed visual effects fields, if they still exist in the xml
516 Boolean allowWhenScreenOff = unsafeBoolean(parser, ALLOW_ATT_SCREEN_OFF);
517 if (allowWhenScreenOff != null) {
518 readSuppressedEffects = true;
519 if (allowWhenScreenOff) {
520 rt.suppressedVisualEffects |= SUPPRESSED_EFFECT_LIGHTS
521 | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
522 }
523 }
524 Boolean allowWhenScreenOn = unsafeBoolean(parser, ALLOW_ATT_SCREEN_ON);
525 if (allowWhenScreenOn != null) {
526 readSuppressedEffects = true;
527 if (allowWhenScreenOn) {
528 rt.suppressedVisualEffects |= SUPPRESSED_EFFECT_PEEK;
529 }
530 }
531 if (readSuppressedEffects) {
532 Slog.d(TAG, "Migrated visual effects to " + rt.suppressedVisualEffects);
533 }
534 } else if (DISALLOW_TAG.equals(tag) && !readSuppressedEffects) {
535 // only read from suppressed visual effects field if we haven't just migrated
536 // the values from allowOn/allowOff, lest we wipe out those settings
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500537 rt.suppressedVisualEffects = safeInt(parser, DISALLOW_ATT_VISUAL_EFFECTS,
538 DEFAULT_SUPPRESSED_VISUAL_EFFECTS);
John Spurlockb2278d62015-04-07 12:47:12 -0400539 } else if (MANUAL_TAG.equals(tag)) {
John Spurlock995a7492015-05-28 22:13:03 -0400540 rt.manualRule = readRuleXml(parser);
John Spurlockb2278d62015-04-07 12:47:12 -0400541 } else if (AUTOMATIC_TAG.equals(tag)) {
542 final String id = parser.getAttributeValue(null, RULE_ATT_ID);
John Spurlock995a7492015-05-28 22:13:03 -0400543 final ZenRule automaticRule = readRuleXml(parser);
John Spurlockb2278d62015-04-07 12:47:12 -0400544 if (id != null && automaticRule != null) {
Julia Reynolds56106ff2015-09-30 14:42:53 -0400545 automaticRule.id = id;
John Spurlockb2278d62015-04-07 12:47:12 -0400546 rt.automaticRules.put(id, automaticRule);
John Spurlock4db0d982014-08-13 09:19:03 -0400547 }
Beverly86d076f2018-04-17 14:44:52 -0400548 } else if (STATE_TAG.equals(tag)) {
549 rt.areChannelsBypassingDnd = safeBoolean(parser,
550 STATE_ATT_CHANNELS_BYPASSING_DND, DEFAULT_CHANNELS_BYPASSING_DND);
John Spurlock056c5192014-04-20 21:52:01 -0400551 }
552 }
553 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400554 throw new IllegalStateException("Failed to reach END_DOCUMENT");
John Spurlock056c5192014-04-20 21:52:01 -0400555 }
556
Beverly4e2f76c2018-03-16 15:43:49 -0400557 /**
558 * Writes XML of current ZenModeConfig
559 * @param out serializer
560 * @param version uses XML_VERSION if version is null
561 * @throws IOException
562 */
563 public void writeXml(XmlSerializer out, Integer version) throws IOException {
John Spurlock056c5192014-04-20 21:52:01 -0400564 out.startTag(null, ZEN_TAG);
Beverly4e2f76c2018-03-16 15:43:49 -0400565 out.attribute(null, ZEN_ATT_VERSION, version == null
566 ? Integer.toString(XML_VERSION) : Integer.toString(version));
John Spurlock21258a32015-05-27 18:22:55 -0400567 out.attribute(null, ZEN_ATT_USER, Integer.toString(user));
John Spurlock056c5192014-04-20 21:52:01 -0400568 out.startTag(null, ALLOW_TAG);
569 out.attribute(null, ALLOW_ATT_CALLS, Boolean.toString(allowCalls));
John Spurlock1d7d2242015-04-10 08:10:22 -0400570 out.attribute(null, ALLOW_ATT_REPEAT_CALLERS, Boolean.toString(allowRepeatCallers));
John Spurlock056c5192014-04-20 21:52:01 -0400571 out.attribute(null, ALLOW_ATT_MESSAGES, Boolean.toString(allowMessages));
John Spurlockfc746f82015-04-03 13:47:14 -0400572 out.attribute(null, ALLOW_ATT_REMINDERS, Boolean.toString(allowReminders));
John Spurlock2dac62c2014-09-19 15:29:06 -0400573 out.attribute(null, ALLOW_ATT_EVENTS, Boolean.toString(allowEvents));
John Spurlocka492d1d2015-05-05 18:30:28 -0400574 out.attribute(null, ALLOW_ATT_CALLS_FROM, Integer.toString(allowCallsFrom));
575 out.attribute(null, ALLOW_ATT_MESSAGES_FROM, Integer.toString(allowMessagesFrom));
Beverly04216872017-09-28 10:55:32 -0400576 out.attribute(null, ALLOW_ATT_ALARMS, Boolean.toString(allowAlarms));
Beverlyd6964762018-02-16 14:07:03 -0500577 out.attribute(null, ALLOW_ATT_MEDIA, Boolean.toString(allowMedia));
578 out.attribute(null, ALLOW_ATT_SYSTEM, Boolean.toString(allowSystem));
John Spurlock056c5192014-04-20 21:52:01 -0400579 out.endTag(null, ALLOW_TAG);
580
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500581 out.startTag(null, DISALLOW_TAG);
582 out.attribute(null, DISALLOW_ATT_VISUAL_EFFECTS, Integer.toString(suppressedVisualEffects));
583 out.endTag(null, DISALLOW_TAG);
584
John Spurlockb2278d62015-04-07 12:47:12 -0400585 if (manualRule != null) {
586 out.startTag(null, MANUAL_TAG);
587 writeRuleXml(manualRule, out);
588 out.endTag(null, MANUAL_TAG);
John Spurlock056c5192014-04-20 21:52:01 -0400589 }
John Spurlockb2278d62015-04-07 12:47:12 -0400590 final int N = automaticRules.size();
591 for (int i = 0; i < N; i++) {
592 final String id = automaticRules.keyAt(i);
593 final ZenRule automaticRule = automaticRules.valueAt(i);
594 out.startTag(null, AUTOMATIC_TAG);
595 out.attribute(null, RULE_ATT_ID, id);
596 writeRuleXml(automaticRule, out);
597 out.endTag(null, AUTOMATIC_TAG);
John Spurlock856edeb2014-06-01 20:36:47 -0400598 }
Beverly86d076f2018-04-17 14:44:52 -0400599
600 out.startTag(null, STATE_TAG);
601 out.attribute(null, STATE_ATT_CHANNELS_BYPASSING_DND,
602 Boolean.toString(areChannelsBypassingDnd));
603 out.endTag(null, STATE_TAG);
604
John Spurlock056c5192014-04-20 21:52:01 -0400605 out.endTag(null, ZEN_TAG);
606 }
607
John Spurlock995a7492015-05-28 22:13:03 -0400608 public static ZenRule readRuleXml(XmlPullParser parser) {
John Spurlockb2278d62015-04-07 12:47:12 -0400609 final ZenRule rt = new ZenRule();
610 rt.enabled = safeBoolean(parser, RULE_ATT_ENABLED, true);
611 rt.snoozing = safeBoolean(parser, RULE_ATT_SNOOZING, false);
612 rt.name = parser.getAttributeValue(null, RULE_ATT_NAME);
613 final String zen = parser.getAttributeValue(null, RULE_ATT_ZEN);
614 rt.zenMode = tryParseZenMode(zen, -1);
615 if (rt.zenMode == -1) {
616 Slog.w(TAG, "Bad zen mode in rule xml:" + zen);
617 return null;
618 }
619 rt.conditionId = safeUri(parser, RULE_ATT_CONDITION_ID);
620 rt.component = safeComponentName(parser, RULE_ATT_COMPONENT);
Julia Reynolds56106ff2015-09-30 14:42:53 -0400621 rt.creationTime = safeLong(parser, RULE_ATT_CREATION_TIME, 0);
Julia Reynolds44ad6ff2016-07-06 09:47:45 -0400622 rt.enabler = parser.getAttributeValue(null, RULE_ATT_ENABLER);
John Spurlockb2278d62015-04-07 12:47:12 -0400623 rt.condition = readConditionXml(parser);
Beverlybf16c142017-11-27 16:21:34 -0500624
625 // all default rules and user created rules updated to zenMode important interruptions
626 if (rt.zenMode != Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
627 && Condition.isValidId(rt.conditionId, SYSTEM_AUTHORITY)) {
628 Slog.i(TAG, "Updating zenMode of automatic rule " + rt.name);
629 rt.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
630 }
John Spurlockd60258f2015-04-30 09:30:52 -0400631 return rt;
John Spurlockb2278d62015-04-07 12:47:12 -0400632 }
633
634 public static void writeRuleXml(ZenRule rule, XmlSerializer out) throws IOException {
635 out.attribute(null, RULE_ATT_ENABLED, Boolean.toString(rule.enabled));
636 out.attribute(null, RULE_ATT_SNOOZING, Boolean.toString(rule.snoozing));
637 if (rule.name != null) {
638 out.attribute(null, RULE_ATT_NAME, rule.name);
639 }
640 out.attribute(null, RULE_ATT_ZEN, Integer.toString(rule.zenMode));
641 if (rule.component != null) {
642 out.attribute(null, RULE_ATT_COMPONENT, rule.component.flattenToString());
643 }
644 if (rule.conditionId != null) {
645 out.attribute(null, RULE_ATT_CONDITION_ID, rule.conditionId.toString());
646 }
Julia Reynolds56106ff2015-09-30 14:42:53 -0400647 out.attribute(null, RULE_ATT_CREATION_TIME, Long.toString(rule.creationTime));
Julia Reynolds44ad6ff2016-07-06 09:47:45 -0400648 if (rule.enabler != null) {
649 out.attribute(null, RULE_ATT_ENABLER, rule.enabler);
650 }
John Spurlockb2278d62015-04-07 12:47:12 -0400651 if (rule.condition != null) {
652 writeConditionXml(rule.condition, out);
653 }
654 }
655
John Spurlock4db0d982014-08-13 09:19:03 -0400656 public static Condition readConditionXml(XmlPullParser parser) {
657 final Uri id = safeUri(parser, CONDITION_ATT_ID);
John Spurlockb2278d62015-04-07 12:47:12 -0400658 if (id == null) return null;
John Spurlock4db0d982014-08-13 09:19:03 -0400659 final String summary = parser.getAttributeValue(null, CONDITION_ATT_SUMMARY);
660 final String line1 = parser.getAttributeValue(null, CONDITION_ATT_LINE1);
661 final String line2 = parser.getAttributeValue(null, CONDITION_ATT_LINE2);
662 final int icon = safeInt(parser, CONDITION_ATT_ICON, -1);
663 final int state = safeInt(parser, CONDITION_ATT_STATE, -1);
664 final int flags = safeInt(parser, CONDITION_ATT_FLAGS, -1);
665 try {
666 return new Condition(id, summary, line1, line2, icon, state, flags);
667 } catch (IllegalArgumentException e) {
668 Slog.w(TAG, "Unable to read condition xml", e);
669 return null;
670 }
671 }
672
673 public static void writeConditionXml(Condition c, XmlSerializer out) throws IOException {
674 out.attribute(null, CONDITION_ATT_ID, c.id.toString());
675 out.attribute(null, CONDITION_ATT_SUMMARY, c.summary);
676 out.attribute(null, CONDITION_ATT_LINE1, c.line1);
677 out.attribute(null, CONDITION_ATT_LINE2, c.line2);
678 out.attribute(null, CONDITION_ATT_ICON, Integer.toString(c.icon));
679 out.attribute(null, CONDITION_ATT_STATE, Integer.toString(c.state));
680 out.attribute(null, CONDITION_ATT_FLAGS, Integer.toString(c.flags));
681 }
682
John Spurlock056c5192014-04-20 21:52:01 -0400683 public static boolean isValidHour(int val) {
684 return val >= 0 && val < 24;
685 }
686
687 public static boolean isValidMinute(int val) {
688 return val >= 0 && val < 60;
689 }
690
John Spurlocka492d1d2015-05-05 18:30:28 -0400691 private static boolean isValidSource(int source) {
692 return source >= SOURCE_ANYONE && source <= MAX_SOURCE;
693 }
694
Julia Reynolds1f580572018-04-27 14:48:36 -0400695 private static Boolean unsafeBoolean(XmlPullParser parser, String att) {
696 final String val = parser.getAttributeValue(null, att);
697 if (TextUtils.isEmpty(val)) return null;
698 return Boolean.parseBoolean(val);
699 }
700
John Spurlock056c5192014-04-20 21:52:01 -0400701 private static boolean safeBoolean(XmlPullParser parser, String att, boolean defValue) {
702 final String val = parser.getAttributeValue(null, att);
Julia Reynolds1998ee52016-02-11 13:49:08 -0500703 return safeBoolean(val, defValue);
704 }
705
706 private static boolean safeBoolean(String val, boolean defValue) {
John Spurlock056c5192014-04-20 21:52:01 -0400707 if (TextUtils.isEmpty(val)) return defValue;
Tobias Thiererb0800dc2016-04-21 17:51:41 +0100708 return Boolean.parseBoolean(val);
John Spurlock056c5192014-04-20 21:52:01 -0400709 }
710
711 private static int safeInt(XmlPullParser parser, String att, int defValue) {
712 final String val = parser.getAttributeValue(null, att);
John Spurlockae641c92014-06-30 18:11:40 -0400713 return tryParseInt(val, defValue);
John Spurlock056c5192014-04-20 21:52:01 -0400714 }
715
John Spurlock3b98b3f2014-05-01 09:08:48 -0400716 private static ComponentName safeComponentName(XmlPullParser parser, String att) {
717 final String val = parser.getAttributeValue(null, att);
718 if (TextUtils.isEmpty(val)) return null;
719 return ComponentName.unflattenFromString(val);
720 }
721
722 private static Uri safeUri(XmlPullParser parser, String att) {
723 final String val = parser.getAttributeValue(null, att);
724 if (TextUtils.isEmpty(val)) return null;
725 return Uri.parse(val);
726 }
727
Julia Reynolds56106ff2015-09-30 14:42:53 -0400728 private static long safeLong(XmlPullParser parser, String att, long defValue) {
729 final String val = parser.getAttributeValue(null, att);
730 return tryParseLong(val, defValue);
731 }
732
John Spurlock056c5192014-04-20 21:52:01 -0400733 @Override
734 public int describeContents() {
735 return 0;
736 }
737
738 public ZenModeConfig copy() {
739 final Parcel parcel = Parcel.obtain();
740 try {
741 writeToParcel(parcel, 0);
742 parcel.setDataPosition(0);
743 return new ZenModeConfig(parcel);
744 } finally {
745 parcel.recycle();
746 }
747 }
748
749 public static final Parcelable.Creator<ZenModeConfig> CREATOR
750 = new Parcelable.Creator<ZenModeConfig>() {
751 @Override
752 public ZenModeConfig createFromParcel(Parcel source) {
753 return new ZenModeConfig(source);
754 }
755
756 @Override
757 public ZenModeConfig[] newArray(int size) {
758 return new ZenModeConfig[size];
759 }
760 };
John Spurlock856edeb2014-06-01 20:36:47 -0400761
John Spurlock1fc476d2015-04-14 16:05:20 -0400762 public Policy toNotificationPolicy() {
763 int priorityCategories = 0;
John Spurlocka492d1d2015-05-05 18:30:28 -0400764 int priorityCallSenders = Policy.PRIORITY_SENDERS_CONTACTS;
765 int priorityMessageSenders = Policy.PRIORITY_SENDERS_CONTACTS;
John Spurlock1fc476d2015-04-14 16:05:20 -0400766 if (allowCalls) {
767 priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS;
768 }
769 if (allowMessages) {
770 priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES;
771 }
772 if (allowEvents) {
773 priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS;
774 }
775 if (allowReminders) {
776 priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS;
777 }
778 if (allowRepeatCallers) {
779 priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS;
780 }
Beverly04216872017-09-28 10:55:32 -0400781 if (allowAlarms) {
782 priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS;
783 }
Beverlyd6964762018-02-16 14:07:03 -0500784 if (allowMedia) {
785 priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA;
786 }
787 if (allowSystem) {
788 priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM;
Beverly04216872017-09-28 10:55:32 -0400789 }
John Spurlocka492d1d2015-05-05 18:30:28 -0400790 priorityCallSenders = sourceToPrioritySenders(allowCallsFrom, priorityCallSenders);
791 priorityMessageSenders = sourceToPrioritySenders(allowMessagesFrom, priorityMessageSenders);
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500792 return new Policy(priorityCategories, priorityCallSenders, priorityMessageSenders,
Beverly86d076f2018-04-17 14:44:52 -0400793 suppressedVisualEffects, areChannelsBypassingDnd
794 ? Policy.STATE_CHANNELS_BYPASSING_DND : 0);
John Spurlocka492d1d2015-05-05 18:30:28 -0400795 }
796
Beverlybe6d3522017-11-20 11:01:59 -0500797 /**
798 * Creates scheduleCalendar from a condition id
799 * @param conditionId
800 * @return ScheduleCalendar with info populated with conditionId
801 */
802 public static ScheduleCalendar toScheduleCalendar(Uri conditionId) {
803 final ScheduleInfo schedule = ZenModeConfig.tryParseScheduleConditionId(conditionId);
804 if (schedule == null || schedule.days == null || schedule.days.length == 0) return null;
805 final ScheduleCalendar sc = new ScheduleCalendar();
806 sc.setSchedule(schedule);
807 sc.setTimeZone(TimeZone.getDefault());
808 return sc;
809 }
810
John Spurlocka492d1d2015-05-05 18:30:28 -0400811 private static int sourceToPrioritySenders(int source, int def) {
812 switch (source) {
813 case SOURCE_ANYONE: return Policy.PRIORITY_SENDERS_ANY;
814 case SOURCE_CONTACT: return Policy.PRIORITY_SENDERS_CONTACTS;
815 case SOURCE_STAR: return Policy.PRIORITY_SENDERS_STARRED;
816 default: return def;
John Spurlock1fc476d2015-04-14 16:05:20 -0400817 }
John Spurlocka492d1d2015-05-05 18:30:28 -0400818 }
819
820 private static int prioritySendersToSource(int prioritySenders, int def) {
821 switch (prioritySenders) {
822 case Policy.PRIORITY_SENDERS_CONTACTS: return SOURCE_CONTACT;
823 case Policy.PRIORITY_SENDERS_STARRED: return SOURCE_STAR;
824 case Policy.PRIORITY_SENDERS_ANY: return SOURCE_ANYONE;
825 default: return def;
826 }
John Spurlock1fc476d2015-04-14 16:05:20 -0400827 }
828
829 public void applyNotificationPolicy(Policy policy) {
830 if (policy == null) return;
Beverly04216872017-09-28 10:55:32 -0400831 allowAlarms = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS) != 0;
Beverlyd6964762018-02-16 14:07:03 -0500832 allowMedia = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MEDIA) != 0;
833 allowSystem = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_SYSTEM) != 0;
John Spurlock1fc476d2015-04-14 16:05:20 -0400834 allowEvents = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_EVENTS) != 0;
835 allowReminders = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REMINDERS) != 0;
Beverly04216872017-09-28 10:55:32 -0400836 allowCalls = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_CALLS) != 0;
837 allowMessages = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MESSAGES) != 0;
John Spurlock1fc476d2015-04-14 16:05:20 -0400838 allowRepeatCallers = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REPEAT_CALLERS)
839 != 0;
John Spurlock80774932015-05-07 17:38:50 -0400840 allowCallsFrom = prioritySendersToSource(policy.priorityCallSenders, allowCallsFrom);
841 allowMessagesFrom = prioritySendersToSource(policy.priorityMessageSenders,
842 allowMessagesFrom);
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500843 if (policy.suppressedVisualEffects != Policy.SUPPRESSED_EFFECTS_UNSET) {
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500844 suppressedVisualEffects = policy.suppressedVisualEffects;
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500845 }
Beverly86d076f2018-04-17 14:44:52 -0400846 if (policy.state != Policy.STATE_UNSET) {
847 areChannelsBypassingDnd = (policy.state & Policy.STATE_CHANNELS_BYPASSING_DND) != 0;
848 }
John Spurlock1fc476d2015-04-14 16:05:20 -0400849 }
850
Selim Cinek9c4a7072014-11-21 17:44:34 +0100851 public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle) {
John Spurlockbbde2672015-05-13 15:42:04 -0400852 return toTimeCondition(context, minutesFromNow, userHandle, false /*shortVersion*/);
853 }
854
855 public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle,
856 boolean shortVersion) {
Jason Monk4dd81462014-09-08 10:13:59 -0400857 final long now = System.currentTimeMillis();
858 final long millis = minutesFromNow == 0 ? ZERO_VALUE_MS : minutesFromNow * MINUTES_MS;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400859 return toTimeCondition(context, now + millis, minutesFromNow, userHandle, shortVersion);
Jason Monk4dd81462014-09-08 10:13:59 -0400860 }
861
Julia Reynolds0842fe82015-10-01 14:34:22 -0400862 public static Condition toTimeCondition(Context context, long time, int minutes,
John Spurlockbbde2672015-05-13 15:42:04 -0400863 int userHandle, boolean shortVersion) {
Julia Reynolds0842fe82015-10-01 14:34:22 -0400864 final int num;
865 String summary, line1, line2;
Julia Reynolds7635ab02017-07-25 08:34:44 -0400866 final CharSequence formattedTime =
867 getFormattedTime(context, time, isToday(time), userHandle);
Julia Reynolds0842fe82015-10-01 14:34:22 -0400868 final Resources res = context.getResources();
John Spurlockc90e6fe2014-10-28 11:21:42 -0400869 if (minutes < 60) {
870 // display as minutes
871 num = minutes;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400872 int summaryResId = shortVersion ? R.plurals.zen_mode_duration_minutes_summary_short
John Spurlockbbde2672015-05-13 15:42:04 -0400873 : R.plurals.zen_mode_duration_minutes_summary;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400874 summary = res.getQuantityString(summaryResId, num, num, formattedTime);
875 int line1ResId = shortVersion ? R.plurals.zen_mode_duration_minutes_short
John Spurlockbbde2672015-05-13 15:42:04 -0400876 : R.plurals.zen_mode_duration_minutes;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400877 line1 = res.getQuantityString(line1ResId, num, num, formattedTime);
878 line2 = res.getString(R.string.zen_mode_until, formattedTime);
879 } else if (minutes < DAY_MINUTES) {
John Spurlockc90e6fe2014-10-28 11:21:42 -0400880 // display as hours
881 num = Math.round(minutes / 60f);
Julia Reynolds0842fe82015-10-01 14:34:22 -0400882 int summaryResId = shortVersion ? R.plurals.zen_mode_duration_hours_summary_short
John Spurlockbbde2672015-05-13 15:42:04 -0400883 : R.plurals.zen_mode_duration_hours_summary;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400884 summary = res.getQuantityString(summaryResId, num, num, formattedTime);
885 int line1ResId = shortVersion ? R.plurals.zen_mode_duration_hours_short
John Spurlockbbde2672015-05-13 15:42:04 -0400886 : R.plurals.zen_mode_duration_hours;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400887 line1 = res.getQuantityString(line1ResId, num, num, formattedTime);
888 line2 = res.getString(R.string.zen_mode_until, formattedTime);
889 } else {
890 // display as day/time
891 summary = line1 = line2 = res.getString(R.string.zen_mode_until, formattedTime);
John Spurlockc90e6fe2014-10-28 11:21:42 -0400892 }
Julia Reynolds7635ab02017-07-25 08:34:44 -0400893 final Uri id = toCountdownConditionId(time, false);
John Spurlockc90e6fe2014-10-28 11:21:42 -0400894 return new Condition(id, summary, line1, line2, 0, Condition.STATE_TRUE,
Jason Monk4dd81462014-09-08 10:13:59 -0400895 Condition.FLAG_RELEVANT_NOW);
896 }
897
Julia Reynolds7635ab02017-07-25 08:34:44 -0400898 /**
899 * Converts countdown to alarm parameters into a condition with user facing summary
900 */
901 public static Condition toNextAlarmCondition(Context context, long alarm,
Julia Reynolds0842fe82015-10-01 14:34:22 -0400902 int userHandle) {
Julia Reynolds7635ab02017-07-25 08:34:44 -0400903 boolean isSameDay = isToday(alarm);
904 final CharSequence formattedTime = getFormattedTime(context, alarm, isSameDay, userHandle);
Julia Reynolds0842fe82015-10-01 14:34:22 -0400905 final Resources res = context.getResources();
Julia Reynolds7635ab02017-07-25 08:34:44 -0400906 final String line1 = res.getString(R.string.zen_mode_until, formattedTime);
907 final Uri id = toCountdownConditionId(alarm, true);
Julia Reynolds0842fe82015-10-01 14:34:22 -0400908 return new Condition(id, "", line1, "", 0, Condition.STATE_TRUE,
909 Condition.FLAG_RELEVANT_NOW);
910 }
911
Beverlybe6d3522017-11-20 11:01:59 -0500912 /**
913 * Creates readable time from time in milliseconds
914 */
915 public static CharSequence getFormattedTime(Context context, long time, boolean isSameDay,
Julia Reynolds7635ab02017-07-25 08:34:44 -0400916 int userHandle) {
917 String skeleton = (!isSameDay ? "EEE " : "")
918 + (DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma");
919 final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
920 return DateFormat.format(pattern, time);
921 }
922
Beverlybe6d3522017-11-20 11:01:59 -0500923 /**
924 * Determines whether a time in milliseconds is today or not
925 */
926 public static boolean isToday(long time) {
Julia Reynolds8e2d2bb2015-10-01 14:34:22 -0400927 GregorianCalendar now = new GregorianCalendar();
928 GregorianCalendar endTime = new GregorianCalendar();
929 endTime.setTimeInMillis(time);
930 if (now.get(Calendar.YEAR) == endTime.get(Calendar.YEAR)
931 && now.get(Calendar.MONTH) == endTime.get(Calendar.MONTH)
932 && now.get(Calendar.DATE) == endTime.get(Calendar.DATE)) {
Julia Reynolds7635ab02017-07-25 08:34:44 -0400933 return true;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400934 }
Julia Reynolds7635ab02017-07-25 08:34:44 -0400935 return false;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400936 }
937
John Spurlockd60258f2015-04-30 09:30:52 -0400938 // ==== Built-in system conditions ====
939
John Spurlock37bc92c2014-11-03 11:01:51 -0500940 public static final String SYSTEM_AUTHORITY = "android";
John Spurlock4db0d982014-08-13 09:19:03 -0400941
John Spurlockd60258f2015-04-30 09:30:52 -0400942 // ==== Built-in system condition: countdown ====
943
John Spurlock530052a2014-11-30 16:26:19 -0500944 public static final String COUNTDOWN_PATH = "countdown";
John Spurlock856edeb2014-06-01 20:36:47 -0400945
Julia Reynolds7635ab02017-07-25 08:34:44 -0400946 public static final String IS_ALARM_PATH = "alarm";
947
948 /**
949 * Converts countdown condition parameters into a condition id.
950 */
951 public static Uri toCountdownConditionId(long time, boolean alarm) {
John Spurlock856edeb2014-06-01 20:36:47 -0400952 return new Uri.Builder().scheme(Condition.SCHEME)
John Spurlock4db0d982014-08-13 09:19:03 -0400953 .authority(SYSTEM_AUTHORITY)
John Spurlock856edeb2014-06-01 20:36:47 -0400954 .appendPath(COUNTDOWN_PATH)
955 .appendPath(Long.toString(time))
Julia Reynolds7635ab02017-07-25 08:34:44 -0400956 .appendPath(IS_ALARM_PATH)
957 .appendPath(Boolean.toString(alarm))
John Spurlock856edeb2014-06-01 20:36:47 -0400958 .build();
959 }
960
961 public static long tryParseCountdownConditionId(Uri conditionId) {
John Spurlock4db0d982014-08-13 09:19:03 -0400962 if (!Condition.isValidId(conditionId, SYSTEM_AUTHORITY)) return 0;
Julia Reynolds7635ab02017-07-25 08:34:44 -0400963 if (conditionId.getPathSegments().size() < 2
John Spurlock856edeb2014-06-01 20:36:47 -0400964 || !COUNTDOWN_PATH.equals(conditionId.getPathSegments().get(0))) return 0;
965 try {
966 return Long.parseLong(conditionId.getPathSegments().get(1));
967 } catch (RuntimeException e) {
968 Slog.w(TAG, "Error parsing countdown condition: " + conditionId, e);
969 return 0;
970 }
971 }
972
Julia Reynolds7635ab02017-07-25 08:34:44 -0400973 /**
974 * Returns whether this condition is a countdown condition.
975 */
John Spurlock856edeb2014-06-01 20:36:47 -0400976 public static boolean isValidCountdownConditionId(Uri conditionId) {
977 return tryParseCountdownConditionId(conditionId) != 0;
978 }
John Spurlock4db0d982014-08-13 09:19:03 -0400979
Julia Reynolds7635ab02017-07-25 08:34:44 -0400980 /**
981 * Returns whether this condition is a countdown to an alarm.
982 */
983 public static boolean isValidCountdownToAlarmConditionId(Uri conditionId) {
984 if (tryParseCountdownConditionId(conditionId) != 0) {
985 if (conditionId.getPathSegments().size() < 4
986 || !IS_ALARM_PATH.equals(conditionId.getPathSegments().get(2))) {
987 return false;
988 }
989 try {
990 return Boolean.parseBoolean(conditionId.getPathSegments().get(3));
991 } catch (RuntimeException e) {
992 Slog.w(TAG, "Error parsing countdown alarm condition: " + conditionId, e);
993 return false;
994 }
995 }
996 return false;
997 }
998
John Spurlockd60258f2015-04-30 09:30:52 -0400999 // ==== Built-in system condition: schedule ====
1000
John Spurlockb2278d62015-04-07 12:47:12 -04001001 public static final String SCHEDULE_PATH = "schedule";
John Spurlock4db0d982014-08-13 09:19:03 -04001002
John Spurlockd60258f2015-04-30 09:30:52 -04001003 public static Uri toScheduleConditionId(ScheduleInfo schedule) {
1004 return new Uri.Builder().scheme(Condition.SCHEME)
1005 .authority(SYSTEM_AUTHORITY)
1006 .appendPath(SCHEDULE_PATH)
1007 .appendQueryParameter("days", toDayList(schedule.days))
1008 .appendQueryParameter("start", schedule.startHour + "." + schedule.startMinute)
1009 .appendQueryParameter("end", schedule.endHour + "." + schedule.endMinute)
Julia Reynolds1998ee52016-02-11 13:49:08 -05001010 .appendQueryParameter("exitAtAlarm", String.valueOf(schedule.exitAtAlarm))
John Spurlockd60258f2015-04-30 09:30:52 -04001011 .build();
1012 }
1013
1014 public static boolean isValidScheduleConditionId(Uri conditionId) {
Julia Reynolds4b318432017-11-22 10:56:18 -05001015 ScheduleInfo info;
1016 try {
1017 info = tryParseScheduleConditionId(conditionId);
1018 } catch (NullPointerException | ArrayIndexOutOfBoundsException e) {
1019 return false;
1020 }
1021
1022 if (info == null || info.days == null || info.days.length == 0) {
1023 return false;
1024 }
1025 return true;
John Spurlockd60258f2015-04-30 09:30:52 -04001026 }
1027
1028 public static ScheduleInfo tryParseScheduleConditionId(Uri conditionId) {
1029 final boolean isSchedule = conditionId != null
Julia Reynolds4bcf9b82018-07-13 15:12:29 -04001030 && Condition.SCHEME.equals(conditionId.getScheme())
1031 && ZenModeConfig.SYSTEM_AUTHORITY.equals(conditionId.getAuthority())
John Spurlockd60258f2015-04-30 09:30:52 -04001032 && conditionId.getPathSegments().size() == 1
Julia Reynolds4bcf9b82018-07-13 15:12:29 -04001033 && ZenModeConfig.SCHEDULE_PATH.equals(conditionId.getPathSegments().get(0));
John Spurlockd60258f2015-04-30 09:30:52 -04001034 if (!isSchedule) return null;
1035 final int[] start = tryParseHourAndMinute(conditionId.getQueryParameter("start"));
1036 final int[] end = tryParseHourAndMinute(conditionId.getQueryParameter("end"));
1037 if (start == null || end == null) return null;
1038 final ScheduleInfo rt = new ScheduleInfo();
1039 rt.days = tryParseDayList(conditionId.getQueryParameter("days"), "\\.");
1040 rt.startHour = start[0];
1041 rt.startMinute = start[1];
1042 rt.endHour = end[0];
1043 rt.endMinute = end[1];
Julia Reynolds1998ee52016-02-11 13:49:08 -05001044 rt.exitAtAlarm = safeBoolean(conditionId.getQueryParameter("exitAtAlarm"), false);
John Spurlockd60258f2015-04-30 09:30:52 -04001045 return rt;
1046 }
1047
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001048 public static ComponentName getScheduleConditionProvider() {
1049 return new ComponentName(SYSTEM_AUTHORITY, "ScheduleConditionProvider");
1050 }
1051
John Spurlockb2278d62015-04-07 12:47:12 -04001052 public static class ScheduleInfo {
1053 public int[] days;
1054 public int startHour;
1055 public int startMinute;
1056 public int endHour;
1057 public int endMinute;
Julia Reynolds1998ee52016-02-11 13:49:08 -05001058 public boolean exitAtAlarm;
1059 public long nextAlarm;
John Spurlockb2278d62015-04-07 12:47:12 -04001060
1061 @Override
1062 public int hashCode() {
1063 return 0;
1064 }
1065
1066 @Override
1067 public boolean equals(Object o) {
1068 if (!(o instanceof ScheduleInfo)) return false;
1069 final ScheduleInfo other = (ScheduleInfo) o;
1070 return toDayList(days).equals(toDayList(other.days))
1071 && startHour == other.startHour
1072 && startMinute == other.startMinute
1073 && endHour == other.endHour
Julia Reynolds1998ee52016-02-11 13:49:08 -05001074 && endMinute == other.endMinute
1075 && exitAtAlarm == other.exitAtAlarm;
John Spurlockb2278d62015-04-07 12:47:12 -04001076 }
1077
1078 public ScheduleInfo copy() {
1079 final ScheduleInfo rt = new ScheduleInfo();
1080 if (days != null) {
1081 rt.days = new int[days.length];
1082 System.arraycopy(days, 0, rt.days, 0, days.length);
1083 }
1084 rt.startHour = startHour;
1085 rt.startMinute = startMinute;
1086 rt.endHour = endHour;
1087 rt.endMinute = endMinute;
Julia Reynolds1998ee52016-02-11 13:49:08 -05001088 rt.exitAtAlarm = exitAtAlarm;
1089 rt.nextAlarm = nextAlarm;
John Spurlockb2278d62015-04-07 12:47:12 -04001090 return rt;
1091 }
Julia Reynolds7f733082016-04-26 14:19:19 -04001092
1093 @Override
1094 public String toString() {
1095 return "ScheduleInfo{" +
1096 "days=" + Arrays.toString(days) +
1097 ", startHour=" + startHour +
1098 ", startMinute=" + startMinute +
1099 ", endHour=" + endHour +
1100 ", endMinute=" + endMinute +
1101 ", exitAtAlarm=" + exitAtAlarm +
Julia Reynoldsfe58f1f2016-07-19 10:18:32 -04001102 ", nextAlarm=" + ts(nextAlarm) +
Julia Reynolds7f733082016-04-26 14:19:19 -04001103 '}';
1104 }
Julia Reynoldsfe58f1f2016-07-19 10:18:32 -04001105
1106 protected static String ts(long time) {
1107 return new Date(time) + " (" + time + ")";
1108 }
John Spurlockb2278d62015-04-07 12:47:12 -04001109 }
1110
John Spurlockd60258f2015-04-30 09:30:52 -04001111 // ==== Built-in system condition: event ====
1112
1113 public static final String EVENT_PATH = "event";
1114
1115 public static Uri toEventConditionId(EventInfo event) {
John Spurlock4db0d982014-08-13 09:19:03 -04001116 return new Uri.Builder().scheme(Condition.SCHEME)
1117 .authority(SYSTEM_AUTHORITY)
John Spurlockd60258f2015-04-30 09:30:52 -04001118 .appendPath(EVENT_PATH)
John Spurlock1b8b22b2015-05-20 09:47:13 -04001119 .appendQueryParameter("userId", Long.toString(event.userId))
John Spurlock995a7492015-05-28 22:13:03 -04001120 .appendQueryParameter("calendar", event.calendar != null ? event.calendar : "")
John Spurlockd60258f2015-04-30 09:30:52 -04001121 .appendQueryParameter("reply", Integer.toString(event.reply))
John Spurlock4db0d982014-08-13 09:19:03 -04001122 .build();
1123 }
1124
John Spurlockd60258f2015-04-30 09:30:52 -04001125 public static boolean isValidEventConditionId(Uri conditionId) {
1126 return tryParseEventConditionId(conditionId) != null;
John Spurlockb2278d62015-04-07 12:47:12 -04001127 }
1128
John Spurlockd60258f2015-04-30 09:30:52 -04001129 public static EventInfo tryParseEventConditionId(Uri conditionId) {
1130 final boolean isEvent = conditionId != null
Julia Reynolds4bcf9b82018-07-13 15:12:29 -04001131 && Condition.SCHEME.equals(conditionId.getScheme())
1132 && ZenModeConfig.SYSTEM_AUTHORITY.equals(conditionId.getAuthority())
John Spurlockb2278d62015-04-07 12:47:12 -04001133 && conditionId.getPathSegments().size() == 1
Julia Reynolds4bcf9b82018-07-13 15:12:29 -04001134 && EVENT_PATH.equals(conditionId.getPathSegments().get(0));
John Spurlockd60258f2015-04-30 09:30:52 -04001135 if (!isEvent) return null;
1136 final EventInfo rt = new EventInfo();
John Spurlock1b8b22b2015-05-20 09:47:13 -04001137 rt.userId = tryParseInt(conditionId.getQueryParameter("userId"), UserHandle.USER_NULL);
John Spurlock995a7492015-05-28 22:13:03 -04001138 rt.calendar = conditionId.getQueryParameter("calendar");
1139 if (TextUtils.isEmpty(rt.calendar) || tryParseLong(rt.calendar, -1L) != -1L) {
1140 rt.calendar = null;
1141 }
John Spurlockd60258f2015-04-30 09:30:52 -04001142 rt.reply = tryParseInt(conditionId.getQueryParameter("reply"), 0);
John Spurlockb2278d62015-04-07 12:47:12 -04001143 return rt;
John Spurlock4db0d982014-08-13 09:19:03 -04001144 }
1145
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001146 public static ComponentName getEventConditionProvider() {
1147 return new ComponentName(SYSTEM_AUTHORITY, "EventConditionProvider");
1148 }
1149
John Spurlockd60258f2015-04-30 09:30:52 -04001150 public static class EventInfo {
John Spurlockd39af2d2015-05-05 09:49:32 -04001151 public static final int REPLY_ANY_EXCEPT_NO = 0;
1152 public static final int REPLY_YES_OR_MAYBE = 1;
John Spurlockd60258f2015-04-30 09:30:52 -04001153 public static final int REPLY_YES = 2;
1154
John Spurlock1b8b22b2015-05-20 09:47:13 -04001155 public int userId = UserHandle.USER_NULL; // USER_NULL = unspecified - use current user
John Spurlock995a7492015-05-28 22:13:03 -04001156 public String calendar; // CalendarContract.Calendars.OWNER_ACCOUNT, or null for any
John Spurlockd60258f2015-04-30 09:30:52 -04001157 public int reply;
1158
1159 @Override
1160 public int hashCode() {
1161 return 0;
1162 }
1163
1164 @Override
1165 public boolean equals(Object o) {
1166 if (!(o instanceof EventInfo)) return false;
1167 final EventInfo other = (EventInfo) o;
John Spurlock1b8b22b2015-05-20 09:47:13 -04001168 return userId == other.userId
John Spurlock995a7492015-05-28 22:13:03 -04001169 && Objects.equals(calendar, other.calendar)
John Spurlockd60258f2015-04-30 09:30:52 -04001170 && reply == other.reply;
1171 }
1172
1173 public EventInfo copy() {
1174 final EventInfo rt = new EventInfo();
John Spurlock1b8b22b2015-05-20 09:47:13 -04001175 rt.userId = userId;
John Spurlockd60258f2015-04-30 09:30:52 -04001176 rt.calendar = calendar;
John Spurlockd60258f2015-04-30 09:30:52 -04001177 rt.reply = reply;
1178 return rt;
1179 }
John Spurlock1b8b22b2015-05-20 09:47:13 -04001180
1181 public static int resolveUserId(int userId) {
1182 return userId == UserHandle.USER_NULL ? ActivityManager.getCurrentUser() : userId;
John Spurlock1b8b22b2015-05-20 09:47:13 -04001183 }
John Spurlockd60258f2015-04-30 09:30:52 -04001184 }
1185
1186 // ==== End built-in system conditions ====
1187
John Spurlock4db0d982014-08-13 09:19:03 -04001188 private static int[] tryParseHourAndMinute(String value) {
1189 if (TextUtils.isEmpty(value)) return null;
1190 final int i = value.indexOf('.');
1191 if (i < 1 || i >= value.length() - 1) return null;
1192 final int hour = tryParseInt(value.substring(0, i), -1);
1193 final int minute = tryParseInt(value.substring(i + 1), -1);
1194 return isValidHour(hour) && isValidMinute(minute) ? new int[] { hour, minute } : null;
1195 }
1196
John Spurlockb2278d62015-04-07 12:47:12 -04001197 private static int tryParseZenMode(String value, int defValue) {
1198 final int rt = tryParseInt(value, defValue);
1199 return Global.isValidZenMode(rt) ? rt : defValue;
John Spurlock4db0d982014-08-13 09:19:03 -04001200 }
1201
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001202 public static String newRuleId() {
John Spurlockb2278d62015-04-07 12:47:12 -04001203 return UUID.randomUUID().toString().replace("-", "");
1204 }
1205
Beverlybe6d3522017-11-20 11:01:59 -05001206 /**
1207 * Gets the name of the app associated with owner
1208 */
1209 public static String getOwnerCaption(Context context, String owner) {
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001210 final PackageManager pm = context.getPackageManager();
1211 try {
1212 final ApplicationInfo info = pm.getApplicationInfo(owner, 0);
1213 if (info != null) {
1214 final CharSequence seq = info.loadLabel(pm);
1215 if (seq != null) {
1216 final String str = seq.toString().trim();
1217 if (str.length() > 0) {
1218 return str;
1219 }
1220 }
1221 }
1222 } catch (Throwable e) {
1223 Slog.w(TAG, "Error loading owner caption", e);
1224 }
1225 return "";
1226 }
1227
John Spurlockb2278d62015-04-07 12:47:12 -04001228 public static String getConditionSummary(Context context, ZenModeConfig config,
John Spurlockbbde2672015-05-13 15:42:04 -04001229 int userHandle, boolean shortVersion) {
1230 return getConditionLine(context, config, userHandle, false /*useLine1*/, shortVersion);
John Spurlockb2278d62015-04-07 12:47:12 -04001231 }
1232
1233 private static String getConditionLine(Context context, ZenModeConfig config,
John Spurlockbbde2672015-05-13 15:42:04 -04001234 int userHandle, boolean useLine1, boolean shortVersion) {
John Spurlockb2278d62015-04-07 12:47:12 -04001235 if (config == null) return "";
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001236 String summary = "";
John Spurlockb2278d62015-04-07 12:47:12 -04001237 if (config.manualRule != null) {
1238 final Uri id = config.manualRule.conditionId;
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001239 if (config.manualRule.enabler != null) {
1240 summary = getOwnerCaption(context, config.manualRule.enabler);
1241 } else {
1242 if (id == null) {
1243 summary = context.getString(com.android.internal.R.string.zen_mode_forever);
1244 } else {
1245 final long time = tryParseCountdownConditionId(id);
1246 Condition c = config.manualRule.condition;
1247 if (time > 0) {
1248 final long now = System.currentTimeMillis();
1249 final long span = time - now;
1250 c = toTimeCondition(context, time, Math.round(span / (float) MINUTES_MS),
1251 userHandle, shortVersion);
1252 }
1253 final String rt = c == null ? "" : useLine1 ? c.line1 : c.summary;
1254 summary = TextUtils.isEmpty(rt) ? "" : rt;
1255 }
John Spurlockb2278d62015-04-07 12:47:12 -04001256 }
John Spurlockb2278d62015-04-07 12:47:12 -04001257 }
John Spurlockb2278d62015-04-07 12:47:12 -04001258 for (ZenRule automaticRule : config.automaticRules.values()) {
John Spurlock39581cc2015-04-10 11:59:01 -04001259 if (automaticRule.isAutomaticActive()) {
John Spurlockb2278d62015-04-07 12:47:12 -04001260 if (summary.isEmpty()) {
1261 summary = automaticRule.name;
1262 } else {
1263 summary = context.getResources()
1264 .getString(R.string.zen_mode_rule_name_combination, summary,
1265 automaticRule.name);
1266 }
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001267
John Spurlockb2278d62015-04-07 12:47:12 -04001268 }
1269 }
1270 return summary;
1271 }
1272
1273 public static class ZenRule implements Parcelable {
1274 public boolean enabled;
1275 public boolean snoozing; // user manually disabled this instance
Julia Reynolds361e82d32016-02-26 18:19:49 -05001276 public String name; // required for automatic
John Spurlockb2278d62015-04-07 12:47:12 -04001277 public int zenMode;
1278 public Uri conditionId; // required for automatic
1279 public Condition condition; // optional
1280 public ComponentName component; // optional
Julia Reynolds56106ff2015-09-30 14:42:53 -04001281 public String id; // required for automatic (unique)
1282 public long creationTime; // required for automatic
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001283 public String enabler; // package name, only used for manual rules.
John Spurlockb2278d62015-04-07 12:47:12 -04001284
1285 public ZenRule() { }
1286
1287 public ZenRule(Parcel source) {
1288 enabled = source.readInt() == 1;
1289 snoozing = source.readInt() == 1;
1290 if (source.readInt() == 1) {
1291 name = source.readString();
1292 }
1293 zenMode = source.readInt();
1294 conditionId = source.readParcelable(null);
1295 condition = source.readParcelable(null);
1296 component = source.readParcelable(null);
Julia Reynolds56106ff2015-09-30 14:42:53 -04001297 if (source.readInt() == 1) {
1298 id = source.readString();
1299 }
1300 creationTime = source.readLong();
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001301 if (source.readInt() == 1) {
1302 enabler = source.readString();
1303 }
John Spurlockb2278d62015-04-07 12:47:12 -04001304 }
John Spurlock4db0d982014-08-13 09:19:03 -04001305
1306 @Override
John Spurlockb2278d62015-04-07 12:47:12 -04001307 public int describeContents() {
John Spurlock4db0d982014-08-13 09:19:03 -04001308 return 0;
1309 }
1310
1311 @Override
John Spurlockb2278d62015-04-07 12:47:12 -04001312 public void writeToParcel(Parcel dest, int flags) {
1313 dest.writeInt(enabled ? 1 : 0);
1314 dest.writeInt(snoozing ? 1 : 0);
1315 if (name != null) {
1316 dest.writeInt(1);
1317 dest.writeString(name);
1318 } else {
1319 dest.writeInt(0);
1320 }
1321 dest.writeInt(zenMode);
1322 dest.writeParcelable(conditionId, 0);
1323 dest.writeParcelable(condition, 0);
1324 dest.writeParcelable(component, 0);
Julia Reynolds56106ff2015-09-30 14:42:53 -04001325 if (id != null) {
1326 dest.writeInt(1);
1327 dest.writeString(id);
1328 } else {
1329 dest.writeInt(0);
1330 }
1331 dest.writeLong(creationTime);
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001332 if (enabler != null) {
1333 dest.writeInt(1);
1334 dest.writeString(enabler);
1335 } else {
1336 dest.writeInt(0);
1337 }
John Spurlockb2278d62015-04-07 12:47:12 -04001338 }
1339
1340 @Override
1341 public String toString() {
1342 return new StringBuilder(ZenRule.class.getSimpleName()).append('[')
Julia Reynolds4bcf9b82018-07-13 15:12:29 -04001343 .append("id=").append(id)
1344 .append(",enabled=").append(String.valueOf(enabled).toUpperCase())
John Spurlockb2278d62015-04-07 12:47:12 -04001345 .append(",snoozing=").append(snoozing)
1346 .append(",name=").append(name)
1347 .append(",zenMode=").append(Global.zenModeToString(zenMode))
1348 .append(",conditionId=").append(conditionId)
1349 .append(",condition=").append(condition)
1350 .append(",component=").append(component)
Julia Reynolds56106ff2015-09-30 14:42:53 -04001351 .append(",creationTime=").append(creationTime)
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001352 .append(",enabler=").append(enabler)
John Spurlockb2278d62015-04-07 12:47:12 -04001353 .append(']').toString();
1354 }
1355
Kweku Adams99546332018-01-24 17:03:50 -08001356 /** @hide */
1357 public void writeToProto(ProtoOutputStream proto, long fieldId) {
1358 final long token = proto.start(fieldId);
1359
1360 proto.write(ZenRuleProto.ID, id);
1361 proto.write(ZenRuleProto.NAME, name);
1362 proto.write(ZenRuleProto.CREATION_TIME_MS, creationTime);
1363 proto.write(ZenRuleProto.ENABLED, enabled);
1364 proto.write(ZenRuleProto.ENABLER, enabler);
1365 proto.write(ZenRuleProto.IS_SNOOZING, snoozing);
1366 proto.write(ZenRuleProto.ZEN_MODE, zenMode);
1367 if (conditionId != null) {
1368 proto.write(ZenRuleProto.CONDITION_ID, conditionId.toString());
1369 }
1370 if (condition != null) {
1371 condition.writeToProto(proto, ZenRuleProto.CONDITION);
1372 }
1373 if (component != null) {
1374 component.writeToProto(proto, ZenRuleProto.COMPONENT);
1375 }
1376
1377 proto.end(token);
1378 }
1379
John Spurlock21258a32015-05-27 18:22:55 -04001380 private static void appendDiff(Diff d, String item, ZenRule from, ZenRule to) {
1381 if (d == null) return;
1382 if (from == null) {
1383 if (to != null) {
1384 d.addLine(item, "insert");
1385 }
1386 return;
1387 }
1388 from.appendDiff(d, item, to);
1389 }
1390
1391 private void appendDiff(Diff d, String item, ZenRule to) {
1392 if (to == null) {
1393 d.addLine(item, "delete");
1394 return;
1395 }
1396 if (enabled != to.enabled) {
1397 d.addLine(item, "enabled", enabled, to.enabled);
1398 }
1399 if (snoozing != to.snoozing) {
1400 d.addLine(item, "snoozing", snoozing, to.snoozing);
1401 }
1402 if (!Objects.equals(name, to.name)) {
1403 d.addLine(item, "name", name, to.name);
1404 }
1405 if (zenMode != to.zenMode) {
1406 d.addLine(item, "zenMode", zenMode, to.zenMode);
1407 }
1408 if (!Objects.equals(conditionId, to.conditionId)) {
1409 d.addLine(item, "conditionId", conditionId, to.conditionId);
1410 }
1411 if (!Objects.equals(condition, to.condition)) {
1412 d.addLine(item, "condition", condition, to.condition);
1413 }
1414 if (!Objects.equals(component, to.component)) {
1415 d.addLine(item, "component", component, to.component);
1416 }
Julia Reynolds56106ff2015-09-30 14:42:53 -04001417 if (!Objects.equals(id, to.id)) {
1418 d.addLine(item, "id", id, to.id);
1419 }
Julia Reynoldsc8e54e82015-11-30 16:43:05 -05001420 if (creationTime != to.creationTime) {
Julia Reynolds56106ff2015-09-30 14:42:53 -04001421 d.addLine(item, "creationTime", creationTime, to.creationTime);
1422 }
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001423 if (enabler != to.enabler) {
1424 d.addLine(item, "enabler", enabler, to.enabler);
1425 }
John Spurlock21258a32015-05-27 18:22:55 -04001426 }
1427
John Spurlockb2278d62015-04-07 12:47:12 -04001428 @Override
John Spurlock4db0d982014-08-13 09:19:03 -04001429 public boolean equals(Object o) {
John Spurlockb2278d62015-04-07 12:47:12 -04001430 if (!(o instanceof ZenRule)) return false;
1431 if (o == this) return true;
1432 final ZenRule other = (ZenRule) o;
1433 return other.enabled == enabled
1434 && other.snoozing == snoozing
1435 && Objects.equals(other.name, name)
1436 && other.zenMode == zenMode
1437 && Objects.equals(other.conditionId, conditionId)
1438 && Objects.equals(other.condition, condition)
Julia Reynolds56106ff2015-09-30 14:42:53 -04001439 && Objects.equals(other.component, component)
1440 && Objects.equals(other.id, id)
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001441 && other.creationTime == creationTime
1442 && Objects.equals(other.enabler, enabler);
John Spurlockb2278d62015-04-07 12:47:12 -04001443 }
1444
1445 @Override
1446 public int hashCode() {
1447 return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001448 component, id, creationTime, enabler);
John Spurlockb2278d62015-04-07 12:47:12 -04001449 }
1450
John Spurlock39581cc2015-04-10 11:59:01 -04001451 public boolean isAutomaticActive() {
1452 return enabled && !snoozing && component != null && isTrueOrUnknown();
1453 }
1454
John Spurlockb2278d62015-04-07 12:47:12 -04001455 public boolean isTrueOrUnknown() {
John Spurlock39581cc2015-04-10 11:59:01 -04001456 return condition != null && (condition.state == Condition.STATE_TRUE
1457 || condition.state == Condition.STATE_UNKNOWN);
John Spurlockb2278d62015-04-07 12:47:12 -04001458 }
1459
1460 public static final Parcelable.Creator<ZenRule> CREATOR
1461 = new Parcelable.Creator<ZenRule>() {
1462 @Override
1463 public ZenRule createFromParcel(Parcel source) {
1464 return new ZenRule(source);
1465 }
1466 @Override
1467 public ZenRule[] newArray(int size) {
1468 return new ZenRule[size];
1469 }
1470 };
1471 }
1472
John Spurlock21258a32015-05-27 18:22:55 -04001473 public static class Diff {
1474 private final ArrayList<String> lines = new ArrayList<>();
1475
1476 @Override
1477 public String toString() {
1478 final StringBuilder sb = new StringBuilder("Diff[");
1479 final int N = lines.size();
1480 for (int i = 0; i < N; i++) {
1481 if (i > 0) {
Julia Reynolds4bcf9b82018-07-13 15:12:29 -04001482 sb.append(",\n");
John Spurlock21258a32015-05-27 18:22:55 -04001483 }
1484 sb.append(lines.get(i));
1485 }
1486 return sb.append(']').toString();
1487 }
1488
1489 private Diff addLine(String item, String action) {
1490 lines.add(item + ":" + action);
1491 return this;
1492 }
1493
1494 public Diff addLine(String item, String subitem, Object from, Object to) {
1495 return addLine(item + "." + subitem, from, to);
1496 }
1497
1498 public Diff addLine(String item, Object from, Object to) {
1499 return addLine(item, from + "->" + to);
1500 }
1501 }
1502
Beverly925cde82018-01-23 09:31:23 -05001503 /**
Beverlyd6964762018-02-16 14:07:03 -05001504 * Determines whether dnd behavior should mute all notification/ringer sounds
1505 * (sounds associated with ringer volume discluding system)
Beverly925cde82018-01-23 09:31:23 -05001506 */
1507 public static boolean areAllPriorityOnlyNotificationZenSoundsMuted(NotificationManager.Policy
1508 policy) {
1509 boolean allowReminders = (policy.priorityCategories
1510 & NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS) != 0;
1511 boolean allowCalls = (policy.priorityCategories
1512 & NotificationManager.Policy.PRIORITY_CATEGORY_CALLS) != 0;
1513 boolean allowMessages = (policy.priorityCategories
1514 & NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) != 0;
1515 boolean allowEvents = (policy.priorityCategories
1516 & NotificationManager.Policy.PRIORITY_CATEGORY_EVENTS) != 0;
1517 boolean allowRepeatCallers = (policy.priorityCategories
1518 & NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS) != 0;
Beverly86d076f2018-04-17 14:44:52 -04001519 boolean areChannelsBypassingDnd = (policy.state & Policy.STATE_CHANNELS_BYPASSING_DND) != 0;
Beverly925cde82018-01-23 09:31:23 -05001520 return !allowReminders && !allowCalls && !allowMessages && !allowEvents
Beverly86d076f2018-04-17 14:44:52 -04001521 && !allowRepeatCallers && !areChannelsBypassingDnd;
Beverly925cde82018-01-23 09:31:23 -05001522 }
1523
1524 /**
Beverlyb9826dd2018-04-12 15:02:45 -04001525 * Determines if DND is currently overriding the ringer
1526 */
1527 public static boolean isZenOverridingRinger(int zen, ZenModeConfig zenConfig) {
Beverlyb9826dd2018-04-12 15:02:45 -04001528 return zen == Global.ZEN_MODE_NO_INTERRUPTIONS
1529 || zen == Global.ZEN_MODE_ALARMS
1530 || (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
1531 && ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(zenConfig));
1532 }
1533
1534 /**
Beverlyd6964762018-02-16 14:07:03 -05001535 * Determines whether dnd behavior should mute all sounds controlled by ringer
Beverly925cde82018-01-23 09:31:23 -05001536 */
1537 public static boolean areAllPriorityOnlyNotificationZenSoundsMuted(ZenModeConfig config) {
1538 return !config.allowReminders && !config.allowCalls && !config.allowMessages
Beverly86d076f2018-04-17 14:44:52 -04001539 && !config.allowEvents && !config.allowRepeatCallers
1540 && !config.areChannelsBypassingDnd;
Beverly925cde82018-01-23 09:31:23 -05001541 }
1542
1543 /**
1544 * Determines whether all dnd mutes all sounds
1545 */
1546 public static boolean areAllZenBehaviorSoundsMuted(ZenModeConfig config) {
Beverlyd6964762018-02-16 14:07:03 -05001547 return !config.allowAlarms && !config.allowMedia && !config.allowSystem
Beverly925cde82018-01-23 09:31:23 -05001548 && areAllPriorityOnlyNotificationZenSoundsMuted(config);
1549 }
Beverlya5effb42018-02-01 17:08:35 -05001550
1551 /**
1552 * Returns a description of the current do not disturb settings from config.
1553 * - If turned on manually and end time is known, returns end time.
Beverlyebcaeeb2018-04-06 14:15:23 -04001554 * - If turned on manually and end time is on forever until turned off, return null if
1555 * describeForeverCondition is false, else return String describing indefinite behavior
Beverlya5effb42018-02-01 17:08:35 -05001556 * - If turned on by an automatic rule, returns the automatic rule name.
1557 * - If on due to an app, returns the app name.
1558 * - If there's a combination of rules/apps that trigger, then shows the one that will
1559 * last the longest if applicable.
Beverlyebcaeeb2018-04-06 14:15:23 -04001560 * @return null if DND is off or describeForeverCondition is false and
1561 * DND is on forever (until turned off)
Beverlya5effb42018-02-01 17:08:35 -05001562 */
Beverlyebcaeeb2018-04-06 14:15:23 -04001563 public static String getDescription(Context context, boolean zenOn, ZenModeConfig config,
1564 boolean describeForeverCondition) {
1565 if (!zenOn || config == null) {
Beverlya5effb42018-02-01 17:08:35 -05001566 return null;
1567 }
1568
1569 String secondaryText = "";
1570 long latestEndTime = -1;
1571
1572 // DND turned on by manual rule
1573 if (config.manualRule != null) {
1574 final Uri id = config.manualRule.conditionId;
1575 if (config.manualRule.enabler != null) {
1576 // app triggered manual rule
1577 String appName = getOwnerCaption(context, config.manualRule.enabler);
1578 if (!appName.isEmpty()) {
1579 secondaryText = appName;
1580 }
1581 } else {
1582 if (id == null) {
1583 // Do not disturb manually triggered to remain on forever until turned off
Beverlyebcaeeb2018-04-06 14:15:23 -04001584 if (describeForeverCondition) {
1585 return context.getString(R.string.zen_mode_forever);
1586 } else {
1587 return null;
1588 }
Beverlya5effb42018-02-01 17:08:35 -05001589 } else {
1590 latestEndTime = tryParseCountdownConditionId(id);
1591 if (latestEndTime > 0) {
1592 final CharSequence formattedTime = getFormattedTime(context,
1593 latestEndTime, isToday(latestEndTime),
1594 context.getUserId());
1595 secondaryText = context.getString(R.string.zen_mode_until, formattedTime);
1596 }
1597 }
1598 }
1599 }
1600
1601 // DND turned on by an automatic rule
1602 for (ZenRule automaticRule : config.automaticRules.values()) {
1603 if (automaticRule.isAutomaticActive()) {
1604 if (isValidEventConditionId(automaticRule.conditionId)
1605 || isValidScheduleConditionId(automaticRule.conditionId)) {
1606 // set text if automatic rule end time is the latest active rule end time
1607 long endTime = parseAutomaticRuleEndTime(context, automaticRule.conditionId);
1608 if (endTime > latestEndTime) {
1609 latestEndTime = endTime;
1610 secondaryText = automaticRule.name;
1611 }
1612 } else {
1613 // set text if 3rd party rule
1614 return automaticRule.name;
1615 }
1616 }
1617 }
1618
1619 return !secondaryText.equals("") ? secondaryText : null;
1620 }
1621
1622 private static long parseAutomaticRuleEndTime(Context context, Uri id) {
1623 if (isValidEventConditionId(id)) {
1624 // cannot look up end times for events
1625 return Long.MAX_VALUE;
1626 }
1627
1628 if (isValidScheduleConditionId(id)) {
1629 ScheduleCalendar schedule = toScheduleCalendar(id);
1630 long endTimeMs = schedule.getNextChangeTime(System.currentTimeMillis());
1631
1632 // check if automatic rule will end on next alarm
1633 if (schedule.exitAtAlarm()) {
1634 long nextAlarm = getNextAlarm(context);
1635 schedule.maybeSetNextAlarm(System.currentTimeMillis(), nextAlarm);
1636 if (schedule.shouldExitForAlarm(endTimeMs)) {
1637 return nextAlarm;
1638 }
1639 }
1640
1641 return endTimeMs;
1642 }
1643
1644 return -1;
1645 }
1646
1647 private static long getNextAlarm(Context context) {
1648 final AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
1649 final AlarmManager.AlarmClockInfo info = alarms.getNextAlarmClock(context.getUserId());
1650 return info != null ? info.getTriggerTime() : 0;
1651 }
John Spurlock056c5192014-04-20 21:52:01 -04001652}