blob: 7b01f7a49be4fdc6e4850cce9a06ff71f58efdac [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
John Spurlock1b8b22b2015-05-20 09:47:13 -040019import android.app.ActivityManager;
Beverlya5effb42018-02-01 17:08:35 -050020import android.app.AlarmManager;
Beverly925cde82018-01-23 09:31:23 -050021import android.app.NotificationManager;
John Spurlock1fc476d2015-04-14 16:05:20 -040022import android.app.NotificationManager.Policy;
John Spurlock3b98b3f2014-05-01 09:08:48 -040023import android.content.ComponentName;
John Spurlockc90e6fe2014-10-28 11:21:42 -040024import android.content.Context;
Julia Reynolds44ad6ff2016-07-06 09:47:45 -040025import android.content.pm.ApplicationInfo;
26import android.content.pm.PackageManager;
Jason Monk4dd81462014-09-08 10:13:59 -040027import android.content.res.Resources;
John Spurlock3b98b3f2014-05-01 09:08:48 -040028import android.net.Uri;
John Spurlock056c5192014-04-20 21:52:01 -040029import android.os.Parcel;
30import android.os.Parcelable;
John Spurlock1b8b22b2015-05-20 09:47:13 -040031import android.os.UserHandle;
John Spurlockb2278d62015-04-07 12:47:12 -040032import android.provider.Settings.Global;
John Spurlock056c5192014-04-20 21:52:01 -040033import android.text.TextUtils;
John Spurlockc90e6fe2014-10-28 11:21:42 -040034import android.text.format.DateFormat;
John Spurlockb2278d62015-04-07 12:47:12 -040035import android.util.ArrayMap;
36import android.util.ArraySet;
John Spurlock856edeb2014-06-01 20:36:47 -040037import android.util.Slog;
Kweku Adams99546332018-01-24 17:03:50 -080038import android.util.proto.ProtoOutputStream;
John Spurlock056c5192014-04-20 21:52:01 -040039
John Spurlockb2278d62015-04-07 12:47:12 -040040import com.android.internal.R;
41
John Spurlock056c5192014-04-20 21:52:01 -040042import org.xmlpull.v1.XmlPullParser;
43import org.xmlpull.v1.XmlPullParserException;
44import org.xmlpull.v1.XmlSerializer;
45
46import java.io.IOException;
John Spurlock3b98b3f2014-05-01 09:08:48 -040047import java.util.ArrayList;
Julia Reynolds7f733082016-04-26 14:19:19 -040048import java.util.Arrays;
John Spurlockae641c92014-06-30 18:11:40 -040049import java.util.Calendar;
Julia Reynoldsfe58f1f2016-07-19 10:18:32 -040050import java.util.Date;
Julia Reynolds8e2d2bb2015-10-01 14:34:22 -040051import java.util.GregorianCalendar;
Beverly26eba872017-12-04 16:30:11 -050052import java.util.List;
John Spurlockc90e6fe2014-10-28 11:21:42 -040053import java.util.Locale;
John Spurlock056c5192014-04-20 21:52:01 -040054import java.util.Objects;
Beverlybe6d3522017-11-20 11:01:59 -050055import java.util.TimeZone;
John Spurlockb2278d62015-04-07 12:47:12 -040056import java.util.UUID;
John Spurlockc90e6fe2014-10-28 11:21:42 -040057
John Spurlock056c5192014-04-20 21:52:01 -040058/**
59 * Persisted configuration for zen mode.
60 *
61 * @hide
62 */
63public class ZenModeConfig implements Parcelable {
John Spurlock856edeb2014-06-01 20:36:47 -040064 private static String TAG = "ZenModeConfig";
John Spurlock056c5192014-04-20 21:52:01 -040065
Chris Wren99f963e2014-05-28 16:52:42 -040066 public static final int SOURCE_ANYONE = 0;
67 public static final int SOURCE_CONTACT = 1;
68 public static final int SOURCE_STAR = 2;
69 public static final int MAX_SOURCE = SOURCE_STAR;
John Spurlocka492d1d2015-05-05 18:30:28 -040070 private static final int DEFAULT_SOURCE = SOURCE_CONTACT;
Chris Wren99f963e2014-05-28 16:52:42 -040071
Beverly26eba872017-12-04 16:30:11 -050072 public static final String EVENTS_DEFAULT_RULE_ID = "EVENTS_DEFAULT_RULE";
73 public static final String EVERY_NIGHT_DEFAULT_RULE_ID = "EVERY_NIGHT_DEFAULT_RULE";
74 public static final List<String> DEFAULT_RULE_IDS = Arrays.asList(EVERY_NIGHT_DEFAULT_RULE_ID,
75 EVENTS_DEFAULT_RULE_ID);
76
John Spurlockae641c92014-06-30 18:11:40 -040077 public static final int[] ALL_DAYS = { Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY,
78 Calendar.WEDNESDAY, Calendar.THURSDAY, Calendar.FRIDAY, Calendar.SATURDAY };
John Spurlockae641c92014-06-30 18:11:40 -040079
John Spurlockd9c75db2015-04-28 11:19:13 -040080 public static final int[] MINUTE_BUCKETS = generateMinuteBuckets();
Jason Monk4dd81462014-09-08 10:13:59 -040081 private static final int SECONDS_MS = 1000;
82 private static final int MINUTES_MS = 60 * SECONDS_MS;
Julia Reynolds0842fe82015-10-01 14:34:22 -040083 private static final int DAY_MINUTES = 24 * 60;
John Spurlock530052a2014-11-30 16:26:19 -050084 private static final int ZERO_VALUE_MS = 10 * SECONDS_MS;
Jason Monk4dd81462014-09-08 10:13:59 -040085
Julia Reynoldsc4e5ecf2018-04-11 11:33:32 -040086 // Default allow categories set in readXml() from default_zen_mode_config.xml,
87 // fallback/upgrade values:
Beverly04216872017-09-28 10:55:32 -040088 private static final boolean DEFAULT_ALLOW_ALARMS = true;
Beverlyd6964762018-02-16 14:07:03 -050089 private static final boolean DEFAULT_ALLOW_MEDIA = true;
90 private static final boolean DEFAULT_ALLOW_SYSTEM = false;
Beverly04216872017-09-28 10:55:32 -040091 private static final boolean DEFAULT_ALLOW_CALLS = false;
John Spurlocka492d1d2015-05-05 18:30:28 -040092 private static final boolean DEFAULT_ALLOW_MESSAGES = false;
Beverly04216872017-09-28 10:55:32 -040093 private static final boolean DEFAULT_ALLOW_REMINDERS = false;
94 private static final boolean DEFAULT_ALLOW_EVENTS = false;
John Spurlock1d7d2242015-04-10 08:10:22 -040095 private static final boolean DEFAULT_ALLOW_REPEAT_CALLERS = false;
Julia Reynoldsccc6ae62018-03-01 16:24:49 -050096 private static final boolean DEFAULT_ALLOW_SCREEN_OFF = false;
97 private static final boolean DEFAULT_ALLOW_SCREEN_ON = false;
98 private static final int DEFAULT_SUPPRESSED_VISUAL_EFFECTS =
99 Policy.getAllSuppressedVisualEffects();
John Spurlock2dac62c2014-09-19 15:29:06 -0400100
Julia Reynoldsc4e5ecf2018-04-11 11:33:32 -0400101 public static final int XML_VERSION = 7;
Julia Reynoldsd1bf5f02017-07-11 10:39:58 -0400102 public static final String ZEN_TAG = "zen";
John Spurlock056c5192014-04-20 21:52:01 -0400103 private static final String ZEN_ATT_VERSION = "version";
John Spurlock21258a32015-05-27 18:22:55 -0400104 private static final String ZEN_ATT_USER = "user";
John Spurlock056c5192014-04-20 21:52:01 -0400105 private static final String ALLOW_TAG = "allow";
Beverly04216872017-09-28 10:55:32 -0400106 private static final String ALLOW_ATT_ALARMS = "alarms";
Beverlyd6964762018-02-16 14:07:03 -0500107 private static final String ALLOW_ATT_MEDIA = "media";
108 private static final String ALLOW_ATT_SYSTEM = "system";
John Spurlock056c5192014-04-20 21:52:01 -0400109 private static final String ALLOW_ATT_CALLS = "calls";
John Spurlock1d7d2242015-04-10 08:10:22 -0400110 private static final String ALLOW_ATT_REPEAT_CALLERS = "repeatCallers";
John Spurlock056c5192014-04-20 21:52:01 -0400111 private static final String ALLOW_ATT_MESSAGES = "messages";
Chris Wren99f963e2014-05-28 16:52:42 -0400112 private static final String ALLOW_ATT_FROM = "from";
John Spurlocka492d1d2015-05-05 18:30:28 -0400113 private static final String ALLOW_ATT_CALLS_FROM = "callsFrom";
114 private static final String ALLOW_ATT_MESSAGES_FROM = "messagesFrom";
John Spurlockfc746f82015-04-03 13:47:14 -0400115 private static final String ALLOW_ATT_REMINDERS = "reminders";
John Spurlock2dac62c2014-09-19 15:29:06 -0400116 private static final String ALLOW_ATT_EVENTS = "events";
Julia Reynoldsd5607292016-02-05 15:25:58 -0500117 private static final String ALLOW_ATT_SCREEN_OFF = "visualScreenOff";
118 private static final String ALLOW_ATT_SCREEN_ON = "visualScreenOn";
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500119 private static final String DISALLOW_TAG = "disallow";
120 private static final String DISALLOW_ATT_VISUAL_EFFECTS = "visualEffects";
John Spurlock056c5192014-04-20 21:52:01 -0400121
John Spurlock3b98b3f2014-05-01 09:08:48 -0400122 private static final String CONDITION_ATT_ID = "id";
John Spurlock4db0d982014-08-13 09:19:03 -0400123 private static final String CONDITION_ATT_SUMMARY = "summary";
124 private static final String CONDITION_ATT_LINE1 = "line1";
125 private static final String CONDITION_ATT_LINE2 = "line2";
126 private static final String CONDITION_ATT_ICON = "icon";
127 private static final String CONDITION_ATT_STATE = "state";
128 private static final String CONDITION_ATT_FLAGS = "flags";
John Spurlock3b98b3f2014-05-01 09:08:48 -0400129
John Spurlockb2278d62015-04-07 12:47:12 -0400130 private static final String MANUAL_TAG = "manual";
131 private static final String AUTOMATIC_TAG = "automatic";
132
John Spurlockd60258f2015-04-30 09:30:52 -0400133 private static final String RULE_ATT_ID = "ruleId";
John Spurlockb2278d62015-04-07 12:47:12 -0400134 private static final String RULE_ATT_ENABLED = "enabled";
135 private static final String RULE_ATT_SNOOZING = "snoozing";
136 private static final String RULE_ATT_NAME = "name";
137 private static final String RULE_ATT_COMPONENT = "component";
138 private static final String RULE_ATT_ZEN = "zen";
139 private static final String RULE_ATT_CONDITION_ID = "conditionId";
Julia Reynolds56106ff2015-09-30 14:42:53 -0400140 private static final String RULE_ATT_CREATION_TIME = "creationTime";
Julia Reynolds44ad6ff2016-07-06 09:47:45 -0400141 private static final String RULE_ATT_ENABLER = "enabler";
John Spurlock856edeb2014-06-01 20:36:47 -0400142
Beverly04216872017-09-28 10:55:32 -0400143 public boolean allowAlarms = DEFAULT_ALLOW_ALARMS;
Beverlyd6964762018-02-16 14:07:03 -0500144 public boolean allowMedia = DEFAULT_ALLOW_MEDIA;
145 public boolean allowSystem = DEFAULT_ALLOW_SYSTEM;
John Spurlocka492d1d2015-05-05 18:30:28 -0400146 public boolean allowCalls = DEFAULT_ALLOW_CALLS;
John Spurlock1d7d2242015-04-10 08:10:22 -0400147 public boolean allowRepeatCallers = DEFAULT_ALLOW_REPEAT_CALLERS;
John Spurlocka492d1d2015-05-05 18:30:28 -0400148 public boolean allowMessages = DEFAULT_ALLOW_MESSAGES;
John Spurlockfc746f82015-04-03 13:47:14 -0400149 public boolean allowReminders = DEFAULT_ALLOW_REMINDERS;
John Spurlock2dac62c2014-09-19 15:29:06 -0400150 public boolean allowEvents = DEFAULT_ALLOW_EVENTS;
John Spurlocka492d1d2015-05-05 18:30:28 -0400151 public int allowCallsFrom = DEFAULT_SOURCE;
152 public int allowMessagesFrom = DEFAULT_SOURCE;
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -0700153 public int user = UserHandle.USER_SYSTEM;
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500154 public int suppressedVisualEffects = DEFAULT_SUPPRESSED_VISUAL_EFFECTS;
Julia Reynoldsd5607292016-02-05 15:25:58 -0500155 public boolean allowWhenScreenOff = DEFAULT_ALLOW_SCREEN_OFF;
156 public boolean allowWhenScreenOn = DEFAULT_ALLOW_SCREEN_ON;
Beverly3bae4e52018-02-07 12:32:02 -0500157 public int version;
John Spurlock056c5192014-04-20 21:52:01 -0400158
John Spurlockb2278d62015-04-07 12:47:12 -0400159 public ZenRule manualRule;
160 public ArrayMap<String, ZenRule> automaticRules = new ArrayMap<>();
John Spurlock056c5192014-04-20 21:52:01 -0400161
162 public ZenModeConfig() { }
163
164 public ZenModeConfig(Parcel source) {
165 allowCalls = source.readInt() == 1;
John Spurlock1d7d2242015-04-10 08:10:22 -0400166 allowRepeatCallers = source.readInt() == 1;
John Spurlock056c5192014-04-20 21:52:01 -0400167 allowMessages = source.readInt() == 1;
John Spurlockfc746f82015-04-03 13:47:14 -0400168 allowReminders = source.readInt() == 1;
John Spurlock2dac62c2014-09-19 15:29:06 -0400169 allowEvents = source.readInt() == 1;
John Spurlocka492d1d2015-05-05 18:30:28 -0400170 allowCallsFrom = source.readInt();
171 allowMessagesFrom = source.readInt();
John Spurlock21258a32015-05-27 18:22:55 -0400172 user = source.readInt();
John Spurlockb2278d62015-04-07 12:47:12 -0400173 manualRule = source.readParcelable(null);
174 final int len = source.readInt();
175 if (len > 0) {
176 final String[] ids = new String[len];
177 final ZenRule[] rules = new ZenRule[len];
178 source.readStringArray(ids);
179 source.readTypedArray(rules, ZenRule.CREATOR);
180 for (int i = 0; i < len; i++) {
181 automaticRules.put(ids[i], rules[i]);
182 }
183 }
Julia Reynoldsd5607292016-02-05 15:25:58 -0500184 allowWhenScreenOff = source.readInt() == 1;
185 allowWhenScreenOn = source.readInt() == 1;
Beverly04216872017-09-28 10:55:32 -0400186 allowAlarms = source.readInt() == 1;
Beverlyd6964762018-02-16 14:07:03 -0500187 allowMedia = source.readInt() == 1;
188 allowSystem = source.readInt() == 1;
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500189 suppressedVisualEffects = source.readInt();
John Spurlock056c5192014-04-20 21:52:01 -0400190 }
191
192 @Override
193 public void writeToParcel(Parcel dest, int flags) {
194 dest.writeInt(allowCalls ? 1 : 0);
John Spurlock1d7d2242015-04-10 08:10:22 -0400195 dest.writeInt(allowRepeatCallers ? 1 : 0);
John Spurlock056c5192014-04-20 21:52:01 -0400196 dest.writeInt(allowMessages ? 1 : 0);
John Spurlockfc746f82015-04-03 13:47:14 -0400197 dest.writeInt(allowReminders ? 1 : 0);
John Spurlock2dac62c2014-09-19 15:29:06 -0400198 dest.writeInt(allowEvents ? 1 : 0);
John Spurlocka492d1d2015-05-05 18:30:28 -0400199 dest.writeInt(allowCallsFrom);
200 dest.writeInt(allowMessagesFrom);
John Spurlock21258a32015-05-27 18:22:55 -0400201 dest.writeInt(user);
John Spurlockb2278d62015-04-07 12:47:12 -0400202 dest.writeParcelable(manualRule, 0);
203 if (!automaticRules.isEmpty()) {
204 final int len = automaticRules.size();
205 final String[] ids = new String[len];
206 final ZenRule[] rules = new ZenRule[len];
207 for (int i = 0; i < len; i++) {
208 ids[i] = automaticRules.keyAt(i);
209 rules[i] = automaticRules.valueAt(i);
210 }
211 dest.writeInt(len);
212 dest.writeStringArray(ids);
213 dest.writeTypedArray(rules, 0);
214 } else {
215 dest.writeInt(0);
216 }
Julia Reynoldsd5607292016-02-05 15:25:58 -0500217 dest.writeInt(allowWhenScreenOff ? 1 : 0);
218 dest.writeInt(allowWhenScreenOn ? 1 : 0);
Beverly04216872017-09-28 10:55:32 -0400219 dest.writeInt(allowAlarms ? 1 : 0);
Beverlyd6964762018-02-16 14:07:03 -0500220 dest.writeInt(allowMedia ? 1 : 0);
221 dest.writeInt(allowSystem ? 1 : 0);
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500222 dest.writeInt(suppressedVisualEffects);
John Spurlock056c5192014-04-20 21:52:01 -0400223 }
224
225 @Override
226 public String toString() {
227 return new StringBuilder(ZenModeConfig.class.getSimpleName()).append('[')
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500228 .append("user=").append(user)
Beverly04216872017-09-28 10:55:32 -0400229 .append(",allowAlarms=").append(allowAlarms)
Beverlyd6964762018-02-16 14:07:03 -0500230 .append(",allowMedia=").append(allowMedia)
231 .append(",allowSystem=").append(allowSystem)
Beverly04216872017-09-28 10:55:32 -0400232 .append(",allowReminders=").append(allowReminders)
233 .append(",allowEvents=").append(allowEvents)
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500234 .append(",allowCalls=").append(allowCalls)
235 .append(",allowRepeatCallers=").append(allowRepeatCallers)
236 .append(",allowMessages=").append(allowMessages)
237 .append(",allowCallsFrom=").append(sourceToString(allowCallsFrom))
238 .append(",allowMessagesFrom=").append(sourceToString(allowMessagesFrom))
Julia Reynoldsd5607292016-02-05 15:25:58 -0500239 .append(",allowWhenScreenOff=").append(allowWhenScreenOff)
240 .append(",allowWhenScreenOn=").append(allowWhenScreenOn)
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500241 .append(",suppressedVisualEffects=").append(suppressedVisualEffects)
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500242 .append(",automaticRules=").append(automaticRules)
243 .append(",manualRule=").append(manualRule)
244 .append(']').toString();
John Spurlock056c5192014-04-20 21:52:01 -0400245 }
246
John Spurlock21258a32015-05-27 18:22:55 -0400247 private Diff diff(ZenModeConfig to) {
248 final Diff d = new Diff();
249 if (to == null) {
250 return d.addLine("config", "delete");
251 }
252 if (user != to.user) {
253 d.addLine("user", user, to.user);
254 }
Beverly04216872017-09-28 10:55:32 -0400255 if (allowAlarms != to.allowAlarms) {
256 d.addLine("allowAlarms", allowAlarms, to.allowAlarms);
257 }
Beverlyd6964762018-02-16 14:07:03 -0500258 if (allowMedia != to.allowMedia) {
259 d.addLine("allowMedia", allowMedia, to.allowMedia);
260 }
261 if (allowSystem != to.allowSystem) {
262 d.addLine("allowSystem", allowSystem, to.allowSystem);
Beverly04216872017-09-28 10:55:32 -0400263 }
John Spurlock21258a32015-05-27 18:22:55 -0400264 if (allowCalls != to.allowCalls) {
265 d.addLine("allowCalls", allowCalls, to.allowCalls);
266 }
Beverly04216872017-09-28 10:55:32 -0400267 if (allowReminders != to.allowReminders) {
268 d.addLine("allowReminders", allowReminders, to.allowReminders);
269 }
270 if (allowEvents != to.allowEvents) {
271 d.addLine("allowEvents", allowEvents, to.allowEvents);
272 }
John Spurlock21258a32015-05-27 18:22:55 -0400273 if (allowRepeatCallers != to.allowRepeatCallers) {
274 d.addLine("allowRepeatCallers", allowRepeatCallers, to.allowRepeatCallers);
275 }
276 if (allowMessages != to.allowMessages) {
277 d.addLine("allowMessages", allowMessages, to.allowMessages);
278 }
279 if (allowCallsFrom != to.allowCallsFrom) {
280 d.addLine("allowCallsFrom", allowCallsFrom, to.allowCallsFrom);
281 }
282 if (allowMessagesFrom != to.allowMessagesFrom) {
283 d.addLine("allowMessagesFrom", allowMessagesFrom, to.allowMessagesFrom);
284 }
Julia Reynoldsd5607292016-02-05 15:25:58 -0500285 if (allowWhenScreenOff != to.allowWhenScreenOff) {
286 d.addLine("allowWhenScreenOff", allowWhenScreenOff, to.allowWhenScreenOff);
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500287 }
Julia Reynoldsd5607292016-02-05 15:25:58 -0500288 if (allowWhenScreenOn != to.allowWhenScreenOn) {
289 d.addLine("allowWhenScreenOn", allowWhenScreenOn, to.allowWhenScreenOn);
Julia Reynolds61721582016-01-05 08:35:25 -0500290 }
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500291 if (suppressedVisualEffects != to.suppressedVisualEffects) {
292 d.addLine("suppressedVisualEffects", suppressedVisualEffects,
293 to.suppressedVisualEffects);
294 }
John Spurlock21258a32015-05-27 18:22:55 -0400295 final ArraySet<String> allRules = new ArraySet<>();
296 addKeys(allRules, automaticRules);
297 addKeys(allRules, to.automaticRules);
298 final int N = allRules.size();
299 for (int i = 0; i < N; i++) {
300 final String rule = allRules.valueAt(i);
301 final ZenRule fromRule = automaticRules != null ? automaticRules.get(rule) : null;
302 final ZenRule toRule = to.automaticRules != null ? to.automaticRules.get(rule) : null;
303 ZenRule.appendDiff(d, "automaticRule[" + rule + "]", fromRule, toRule);
304 }
305 ZenRule.appendDiff(d, "manualRule", manualRule, to.manualRule);
306 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
Julia Reynoldsd5607292016-02-05 15:25:58 -0500395 && other.allowWhenScreenOff == allowWhenScreenOff
396 && other.allowWhenScreenOn == allowWhenScreenOn
John Spurlock21258a32015-05-27 18:22:55 -0400397 && other.user == user
John Spurlockb2278d62015-04-07 12:47:12 -0400398 && Objects.equals(other.automaticRules, automaticRules)
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500399 && Objects.equals(other.manualRule, manualRule)
400 && other.suppressedVisualEffects == suppressedVisualEffects;
John Spurlock056c5192014-04-20 21:52:01 -0400401 }
402
403 @Override
404 public int hashCode() {
Beverlyd6964762018-02-16 14:07:03 -0500405 return Objects.hash(allowAlarms, allowMedia, allowSystem, allowCalls,
Beverly04216872017-09-28 10:55:32 -0400406 allowRepeatCallers, allowMessages,
407 allowCallsFrom, allowMessagesFrom, allowReminders, allowEvents,
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500408 allowWhenScreenOff, allowWhenScreenOn, user, automaticRules, manualRule,
409 suppressedVisualEffects);
John Spurlock056c5192014-04-20 21:52:01 -0400410 }
411
John Spurlockb2278d62015-04-07 12:47:12 -0400412 private static String toDayList(int[] days) {
413 if (days == null || days.length == 0) return "";
414 final StringBuilder sb = new StringBuilder();
415 for (int i = 0; i < days.length; i++) {
416 if (i > 0) sb.append('.');
417 sb.append(days[i]);
418 }
419 return sb.toString();
John Spurlockae641c92014-06-30 18:11:40 -0400420 }
421
John Spurlockb2278d62015-04-07 12:47:12 -0400422 private static int[] tryParseDayList(String dayList, String sep) {
423 if (dayList == null) return null;
424 final String[] tokens = dayList.split(sep);
John Spurlockae641c92014-06-30 18:11:40 -0400425 if (tokens.length == 0) return null;
426 final int[] rt = new int[tokens.length];
427 for (int i = 0; i < tokens.length; i++) {
428 final int day = tryParseInt(tokens[i], -1);
429 if (day == -1) return null;
430 rt[i] = day;
431 }
432 return rt;
433 }
434
435 private static int tryParseInt(String value, int defValue) {
436 if (TextUtils.isEmpty(value)) return defValue;
437 try {
Narayan Kamatha09b4d22016-04-15 18:32:45 +0100438 return Integer.parseInt(value);
John Spurlockae641c92014-06-30 18:11:40 -0400439 } catch (NumberFormatException e) {
440 return defValue;
441 }
John Spurlock056c5192014-04-20 21:52:01 -0400442 }
443
John Spurlockd60258f2015-04-30 09:30:52 -0400444 private static long tryParseLong(String value, long defValue) {
445 if (TextUtils.isEmpty(value)) return defValue;
446 try {
Tobias Thierer28532d02016-04-21 14:52:10 +0100447 return Long.parseLong(value);
John Spurlockd60258f2015-04-30 09:30:52 -0400448 } catch (NumberFormatException e) {
449 return defValue;
450 }
451 }
452
Julia Reynolds206c7e92016-09-15 10:38:03 -0400453 public static ZenModeConfig readXml(XmlPullParser parser)
John Spurlock056c5192014-04-20 21:52:01 -0400454 throws XmlPullParserException, IOException {
455 int type = parser.getEventType();
456 if (type != XmlPullParser.START_TAG) return null;
457 String tag = parser.getName();
458 if (!ZEN_TAG.equals(tag)) return null;
459 final ZenModeConfig rt = new ZenModeConfig();
Beverly3bae4e52018-02-07 12:32:02 -0500460 rt.version = safeInt(parser, ZEN_ATT_VERSION, XML_VERSION);
John Spurlock21258a32015-05-27 18:22:55 -0400461 rt.user = safeInt(parser, ZEN_ATT_USER, rt.user);
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 {
489 rt.allowCallsFrom = DEFAULT_SOURCE;
490 rt.allowMessagesFrom = DEFAULT_SOURCE;
Chris Wren99f963e2014-05-28 16:52:42 -0400491 }
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500492 // continue to read even though we now have suppressedVisualEffects, in case
493 // we need to revert to users' previous settings
Julia Reynoldsd5607292016-02-05 15:25:58 -0500494 rt.allowWhenScreenOff =
495 safeBoolean(parser, ALLOW_ATT_SCREEN_OFF, DEFAULT_ALLOW_SCREEN_OFF);
496 rt.allowWhenScreenOn =
Julia Reynolds61721582016-01-05 08:35:25 -0500497 safeBoolean(parser, ALLOW_ATT_SCREEN_ON, DEFAULT_ALLOW_SCREEN_ON);
Beverly04216872017-09-28 10:55:32 -0400498 rt.allowAlarms = safeBoolean(parser, ALLOW_ATT_ALARMS, DEFAULT_ALLOW_ALARMS);
Beverlyd6964762018-02-16 14:07:03 -0500499 rt.allowMedia = safeBoolean(parser, ALLOW_ATT_MEDIA,
500 DEFAULT_ALLOW_MEDIA);
501 rt.allowSystem = safeBoolean(parser, ALLOW_ATT_SYSTEM, DEFAULT_ALLOW_SYSTEM);
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500502 } else if (DISALLOW_TAG.equals(tag)) {
503 rt.suppressedVisualEffects = safeInt(parser, DISALLOW_ATT_VISUAL_EFFECTS,
504 DEFAULT_SUPPRESSED_VISUAL_EFFECTS);
John Spurlockb2278d62015-04-07 12:47:12 -0400505 } else if (MANUAL_TAG.equals(tag)) {
John Spurlock995a7492015-05-28 22:13:03 -0400506 rt.manualRule = readRuleXml(parser);
John Spurlockb2278d62015-04-07 12:47:12 -0400507 } else if (AUTOMATIC_TAG.equals(tag)) {
508 final String id = parser.getAttributeValue(null, RULE_ATT_ID);
John Spurlock995a7492015-05-28 22:13:03 -0400509 final ZenRule automaticRule = readRuleXml(parser);
John Spurlockb2278d62015-04-07 12:47:12 -0400510 if (id != null && automaticRule != null) {
Julia Reynolds56106ff2015-09-30 14:42:53 -0400511 automaticRule.id = id;
John Spurlockb2278d62015-04-07 12:47:12 -0400512 rt.automaticRules.put(id, automaticRule);
John Spurlock4db0d982014-08-13 09:19:03 -0400513 }
John Spurlock056c5192014-04-20 21:52:01 -0400514 }
515 }
516 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400517 throw new IllegalStateException("Failed to reach END_DOCUMENT");
John Spurlock056c5192014-04-20 21:52:01 -0400518 }
519
Beverly4e2f76c2018-03-16 15:43:49 -0400520 /**
521 * Writes XML of current ZenModeConfig
522 * @param out serializer
523 * @param version uses XML_VERSION if version is null
524 * @throws IOException
525 */
526 public void writeXml(XmlSerializer out, Integer version) throws IOException {
John Spurlock056c5192014-04-20 21:52:01 -0400527 out.startTag(null, ZEN_TAG);
Beverly4e2f76c2018-03-16 15:43:49 -0400528 out.attribute(null, ZEN_ATT_VERSION, version == null
529 ? Integer.toString(XML_VERSION) : Integer.toString(version));
John Spurlock21258a32015-05-27 18:22:55 -0400530 out.attribute(null, ZEN_ATT_USER, Integer.toString(user));
John Spurlock056c5192014-04-20 21:52:01 -0400531 out.startTag(null, ALLOW_TAG);
532 out.attribute(null, ALLOW_ATT_CALLS, Boolean.toString(allowCalls));
John Spurlock1d7d2242015-04-10 08:10:22 -0400533 out.attribute(null, ALLOW_ATT_REPEAT_CALLERS, Boolean.toString(allowRepeatCallers));
John Spurlock056c5192014-04-20 21:52:01 -0400534 out.attribute(null, ALLOW_ATT_MESSAGES, Boolean.toString(allowMessages));
John Spurlockfc746f82015-04-03 13:47:14 -0400535 out.attribute(null, ALLOW_ATT_REMINDERS, Boolean.toString(allowReminders));
John Spurlock2dac62c2014-09-19 15:29:06 -0400536 out.attribute(null, ALLOW_ATT_EVENTS, Boolean.toString(allowEvents));
John Spurlocka492d1d2015-05-05 18:30:28 -0400537 out.attribute(null, ALLOW_ATT_CALLS_FROM, Integer.toString(allowCallsFrom));
538 out.attribute(null, ALLOW_ATT_MESSAGES_FROM, Integer.toString(allowMessagesFrom));
Julia Reynoldsd5607292016-02-05 15:25:58 -0500539 out.attribute(null, ALLOW_ATT_SCREEN_OFF, Boolean.toString(allowWhenScreenOff));
540 out.attribute(null, ALLOW_ATT_SCREEN_ON, Boolean.toString(allowWhenScreenOn));
Beverly04216872017-09-28 10:55:32 -0400541 out.attribute(null, ALLOW_ATT_ALARMS, Boolean.toString(allowAlarms));
Beverlyd6964762018-02-16 14:07:03 -0500542 out.attribute(null, ALLOW_ATT_MEDIA, Boolean.toString(allowMedia));
543 out.attribute(null, ALLOW_ATT_SYSTEM, Boolean.toString(allowSystem));
John Spurlock056c5192014-04-20 21:52:01 -0400544 out.endTag(null, ALLOW_TAG);
545
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500546 out.startTag(null, DISALLOW_TAG);
547 out.attribute(null, DISALLOW_ATT_VISUAL_EFFECTS, Integer.toString(suppressedVisualEffects));
548 out.endTag(null, DISALLOW_TAG);
549
John Spurlockb2278d62015-04-07 12:47:12 -0400550 if (manualRule != null) {
551 out.startTag(null, MANUAL_TAG);
552 writeRuleXml(manualRule, out);
553 out.endTag(null, MANUAL_TAG);
John Spurlock056c5192014-04-20 21:52:01 -0400554 }
John Spurlockb2278d62015-04-07 12:47:12 -0400555 final int N = automaticRules.size();
556 for (int i = 0; i < N; i++) {
557 final String id = automaticRules.keyAt(i);
558 final ZenRule automaticRule = automaticRules.valueAt(i);
559 out.startTag(null, AUTOMATIC_TAG);
560 out.attribute(null, RULE_ATT_ID, id);
561 writeRuleXml(automaticRule, out);
562 out.endTag(null, AUTOMATIC_TAG);
John Spurlock856edeb2014-06-01 20:36:47 -0400563 }
John Spurlock056c5192014-04-20 21:52:01 -0400564 out.endTag(null, ZEN_TAG);
565 }
566
John Spurlock995a7492015-05-28 22:13:03 -0400567 public static ZenRule readRuleXml(XmlPullParser parser) {
John Spurlockb2278d62015-04-07 12:47:12 -0400568 final ZenRule rt = new ZenRule();
569 rt.enabled = safeBoolean(parser, RULE_ATT_ENABLED, true);
570 rt.snoozing = safeBoolean(parser, RULE_ATT_SNOOZING, false);
571 rt.name = parser.getAttributeValue(null, RULE_ATT_NAME);
572 final String zen = parser.getAttributeValue(null, RULE_ATT_ZEN);
573 rt.zenMode = tryParseZenMode(zen, -1);
574 if (rt.zenMode == -1) {
575 Slog.w(TAG, "Bad zen mode in rule xml:" + zen);
576 return null;
577 }
578 rt.conditionId = safeUri(parser, RULE_ATT_CONDITION_ID);
579 rt.component = safeComponentName(parser, RULE_ATT_COMPONENT);
Julia Reynolds56106ff2015-09-30 14:42:53 -0400580 rt.creationTime = safeLong(parser, RULE_ATT_CREATION_TIME, 0);
Julia Reynolds44ad6ff2016-07-06 09:47:45 -0400581 rt.enabler = parser.getAttributeValue(null, RULE_ATT_ENABLER);
John Spurlockb2278d62015-04-07 12:47:12 -0400582 rt.condition = readConditionXml(parser);
Beverlybf16c142017-11-27 16:21:34 -0500583
584 // all default rules and user created rules updated to zenMode important interruptions
585 if (rt.zenMode != Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
586 && Condition.isValidId(rt.conditionId, SYSTEM_AUTHORITY)) {
587 Slog.i(TAG, "Updating zenMode of automatic rule " + rt.name);
588 rt.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
589 }
John Spurlockd60258f2015-04-30 09:30:52 -0400590 return rt;
John Spurlockb2278d62015-04-07 12:47:12 -0400591 }
592
593 public static void writeRuleXml(ZenRule rule, XmlSerializer out) throws IOException {
594 out.attribute(null, RULE_ATT_ENABLED, Boolean.toString(rule.enabled));
595 out.attribute(null, RULE_ATT_SNOOZING, Boolean.toString(rule.snoozing));
596 if (rule.name != null) {
597 out.attribute(null, RULE_ATT_NAME, rule.name);
598 }
599 out.attribute(null, RULE_ATT_ZEN, Integer.toString(rule.zenMode));
600 if (rule.component != null) {
601 out.attribute(null, RULE_ATT_COMPONENT, rule.component.flattenToString());
602 }
603 if (rule.conditionId != null) {
604 out.attribute(null, RULE_ATT_CONDITION_ID, rule.conditionId.toString());
605 }
Julia Reynolds56106ff2015-09-30 14:42:53 -0400606 out.attribute(null, RULE_ATT_CREATION_TIME, Long.toString(rule.creationTime));
Julia Reynolds44ad6ff2016-07-06 09:47:45 -0400607 if (rule.enabler != null) {
608 out.attribute(null, RULE_ATT_ENABLER, rule.enabler);
609 }
John Spurlockb2278d62015-04-07 12:47:12 -0400610 if (rule.condition != null) {
611 writeConditionXml(rule.condition, out);
612 }
613 }
614
John Spurlock4db0d982014-08-13 09:19:03 -0400615 public static Condition readConditionXml(XmlPullParser parser) {
616 final Uri id = safeUri(parser, CONDITION_ATT_ID);
John Spurlockb2278d62015-04-07 12:47:12 -0400617 if (id == null) return null;
John Spurlock4db0d982014-08-13 09:19:03 -0400618 final String summary = parser.getAttributeValue(null, CONDITION_ATT_SUMMARY);
619 final String line1 = parser.getAttributeValue(null, CONDITION_ATT_LINE1);
620 final String line2 = parser.getAttributeValue(null, CONDITION_ATT_LINE2);
621 final int icon = safeInt(parser, CONDITION_ATT_ICON, -1);
622 final int state = safeInt(parser, CONDITION_ATT_STATE, -1);
623 final int flags = safeInt(parser, CONDITION_ATT_FLAGS, -1);
624 try {
625 return new Condition(id, summary, line1, line2, icon, state, flags);
626 } catch (IllegalArgumentException e) {
627 Slog.w(TAG, "Unable to read condition xml", e);
628 return null;
629 }
630 }
631
632 public static void writeConditionXml(Condition c, XmlSerializer out) throws IOException {
633 out.attribute(null, CONDITION_ATT_ID, c.id.toString());
634 out.attribute(null, CONDITION_ATT_SUMMARY, c.summary);
635 out.attribute(null, CONDITION_ATT_LINE1, c.line1);
636 out.attribute(null, CONDITION_ATT_LINE2, c.line2);
637 out.attribute(null, CONDITION_ATT_ICON, Integer.toString(c.icon));
638 out.attribute(null, CONDITION_ATT_STATE, Integer.toString(c.state));
639 out.attribute(null, CONDITION_ATT_FLAGS, Integer.toString(c.flags));
640 }
641
John Spurlock056c5192014-04-20 21:52:01 -0400642 public static boolean isValidHour(int val) {
643 return val >= 0 && val < 24;
644 }
645
646 public static boolean isValidMinute(int val) {
647 return val >= 0 && val < 60;
648 }
649
John Spurlocka492d1d2015-05-05 18:30:28 -0400650 private static boolean isValidSource(int source) {
651 return source >= SOURCE_ANYONE && source <= MAX_SOURCE;
652 }
653
John Spurlock056c5192014-04-20 21:52:01 -0400654 private static boolean safeBoolean(XmlPullParser parser, String att, boolean defValue) {
655 final String val = parser.getAttributeValue(null, att);
Julia Reynolds1998ee52016-02-11 13:49:08 -0500656 return safeBoolean(val, defValue);
657 }
658
659 private static boolean safeBoolean(String val, boolean defValue) {
John Spurlock056c5192014-04-20 21:52:01 -0400660 if (TextUtils.isEmpty(val)) return defValue;
Tobias Thiererb0800dc2016-04-21 17:51:41 +0100661 return Boolean.parseBoolean(val);
John Spurlock056c5192014-04-20 21:52:01 -0400662 }
663
664 private static int safeInt(XmlPullParser parser, String att, int defValue) {
665 final String val = parser.getAttributeValue(null, att);
John Spurlockae641c92014-06-30 18:11:40 -0400666 return tryParseInt(val, defValue);
John Spurlock056c5192014-04-20 21:52:01 -0400667 }
668
John Spurlock3b98b3f2014-05-01 09:08:48 -0400669 private static ComponentName safeComponentName(XmlPullParser parser, String att) {
670 final String val = parser.getAttributeValue(null, att);
671 if (TextUtils.isEmpty(val)) return null;
672 return ComponentName.unflattenFromString(val);
673 }
674
675 private static Uri safeUri(XmlPullParser parser, String att) {
676 final String val = parser.getAttributeValue(null, att);
677 if (TextUtils.isEmpty(val)) return null;
678 return Uri.parse(val);
679 }
680
Julia Reynolds56106ff2015-09-30 14:42:53 -0400681 private static long safeLong(XmlPullParser parser, String att, long defValue) {
682 final String val = parser.getAttributeValue(null, att);
683 return tryParseLong(val, defValue);
684 }
685
John Spurlock056c5192014-04-20 21:52:01 -0400686 @Override
687 public int describeContents() {
688 return 0;
689 }
690
691 public ZenModeConfig copy() {
692 final Parcel parcel = Parcel.obtain();
693 try {
694 writeToParcel(parcel, 0);
695 parcel.setDataPosition(0);
696 return new ZenModeConfig(parcel);
697 } finally {
698 parcel.recycle();
699 }
700 }
701
702 public static final Parcelable.Creator<ZenModeConfig> CREATOR
703 = new Parcelable.Creator<ZenModeConfig>() {
704 @Override
705 public ZenModeConfig createFromParcel(Parcel source) {
706 return new ZenModeConfig(source);
707 }
708
709 @Override
710 public ZenModeConfig[] newArray(int size) {
711 return new ZenModeConfig[size];
712 }
713 };
John Spurlock856edeb2014-06-01 20:36:47 -0400714
John Spurlock1fc476d2015-04-14 16:05:20 -0400715 public Policy toNotificationPolicy() {
716 int priorityCategories = 0;
John Spurlocka492d1d2015-05-05 18:30:28 -0400717 int priorityCallSenders = Policy.PRIORITY_SENDERS_CONTACTS;
718 int priorityMessageSenders = Policy.PRIORITY_SENDERS_CONTACTS;
John Spurlock1fc476d2015-04-14 16:05:20 -0400719 if (allowCalls) {
720 priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS;
721 }
722 if (allowMessages) {
723 priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES;
724 }
725 if (allowEvents) {
726 priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS;
727 }
728 if (allowReminders) {
729 priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS;
730 }
731 if (allowRepeatCallers) {
732 priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS;
733 }
Beverly04216872017-09-28 10:55:32 -0400734 if (allowAlarms) {
735 priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS;
736 }
Beverlyd6964762018-02-16 14:07:03 -0500737 if (allowMedia) {
738 priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA;
739 }
740 if (allowSystem) {
741 priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM;
Beverly04216872017-09-28 10:55:32 -0400742 }
John Spurlocka492d1d2015-05-05 18:30:28 -0400743 priorityCallSenders = sourceToPrioritySenders(allowCallsFrom, priorityCallSenders);
744 priorityMessageSenders = sourceToPrioritySenders(allowMessagesFrom, priorityMessageSenders);
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500745 return new Policy(priorityCategories, priorityCallSenders, priorityMessageSenders,
746 suppressedVisualEffects);
John Spurlocka492d1d2015-05-05 18:30:28 -0400747 }
748
Beverlybe6d3522017-11-20 11:01:59 -0500749 /**
750 * Creates scheduleCalendar from a condition id
751 * @param conditionId
752 * @return ScheduleCalendar with info populated with conditionId
753 */
754 public static ScheduleCalendar toScheduleCalendar(Uri conditionId) {
755 final ScheduleInfo schedule = ZenModeConfig.tryParseScheduleConditionId(conditionId);
756 if (schedule == null || schedule.days == null || schedule.days.length == 0) return null;
757 final ScheduleCalendar sc = new ScheduleCalendar();
758 sc.setSchedule(schedule);
759 sc.setTimeZone(TimeZone.getDefault());
760 return sc;
761 }
762
John Spurlocka492d1d2015-05-05 18:30:28 -0400763 private static int sourceToPrioritySenders(int source, int def) {
764 switch (source) {
765 case SOURCE_ANYONE: return Policy.PRIORITY_SENDERS_ANY;
766 case SOURCE_CONTACT: return Policy.PRIORITY_SENDERS_CONTACTS;
767 case SOURCE_STAR: return Policy.PRIORITY_SENDERS_STARRED;
768 default: return def;
John Spurlock1fc476d2015-04-14 16:05:20 -0400769 }
John Spurlocka492d1d2015-05-05 18:30:28 -0400770 }
771
772 private static int prioritySendersToSource(int prioritySenders, int def) {
773 switch (prioritySenders) {
774 case Policy.PRIORITY_SENDERS_CONTACTS: return SOURCE_CONTACT;
775 case Policy.PRIORITY_SENDERS_STARRED: return SOURCE_STAR;
776 case Policy.PRIORITY_SENDERS_ANY: return SOURCE_ANYONE;
777 default: return def;
778 }
John Spurlock1fc476d2015-04-14 16:05:20 -0400779 }
780
781 public void applyNotificationPolicy(Policy policy) {
782 if (policy == null) return;
Beverly04216872017-09-28 10:55:32 -0400783 allowAlarms = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS) != 0;
Beverlyd6964762018-02-16 14:07:03 -0500784 allowMedia = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MEDIA) != 0;
785 allowSystem = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_SYSTEM) != 0;
John Spurlock1fc476d2015-04-14 16:05:20 -0400786 allowEvents = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_EVENTS) != 0;
787 allowReminders = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REMINDERS) != 0;
Beverly04216872017-09-28 10:55:32 -0400788 allowCalls = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_CALLS) != 0;
789 allowMessages = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MESSAGES) != 0;
John Spurlock1fc476d2015-04-14 16:05:20 -0400790 allowRepeatCallers = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REPEAT_CALLERS)
791 != 0;
John Spurlock80774932015-05-07 17:38:50 -0400792 allowCallsFrom = prioritySendersToSource(policy.priorityCallSenders, allowCallsFrom);
793 allowMessagesFrom = prioritySendersToSource(policy.priorityMessageSenders,
794 allowMessagesFrom);
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500795 if (policy.suppressedVisualEffects != Policy.SUPPRESSED_EFFECTS_UNSET) {
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500796 suppressedVisualEffects = policy.suppressedVisualEffects;
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500797 }
John Spurlock1fc476d2015-04-14 16:05:20 -0400798 }
799
Selim Cinek9c4a7072014-11-21 17:44:34 +0100800 public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle) {
John Spurlockbbde2672015-05-13 15:42:04 -0400801 return toTimeCondition(context, minutesFromNow, userHandle, false /*shortVersion*/);
802 }
803
804 public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle,
805 boolean shortVersion) {
Jason Monk4dd81462014-09-08 10:13:59 -0400806 final long now = System.currentTimeMillis();
807 final long millis = minutesFromNow == 0 ? ZERO_VALUE_MS : minutesFromNow * MINUTES_MS;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400808 return toTimeCondition(context, now + millis, minutesFromNow, userHandle, shortVersion);
Jason Monk4dd81462014-09-08 10:13:59 -0400809 }
810
Julia Reynolds0842fe82015-10-01 14:34:22 -0400811 public static Condition toTimeCondition(Context context, long time, int minutes,
John Spurlockbbde2672015-05-13 15:42:04 -0400812 int userHandle, boolean shortVersion) {
Julia Reynolds0842fe82015-10-01 14:34:22 -0400813 final int num;
814 String summary, line1, line2;
Julia Reynolds7635ab02017-07-25 08:34:44 -0400815 final CharSequence formattedTime =
816 getFormattedTime(context, time, isToday(time), userHandle);
Julia Reynolds0842fe82015-10-01 14:34:22 -0400817 final Resources res = context.getResources();
John Spurlockc90e6fe2014-10-28 11:21:42 -0400818 if (minutes < 60) {
819 // display as minutes
820 num = minutes;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400821 int summaryResId = shortVersion ? R.plurals.zen_mode_duration_minutes_summary_short
John Spurlockbbde2672015-05-13 15:42:04 -0400822 : R.plurals.zen_mode_duration_minutes_summary;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400823 summary = res.getQuantityString(summaryResId, num, num, formattedTime);
824 int line1ResId = shortVersion ? R.plurals.zen_mode_duration_minutes_short
John Spurlockbbde2672015-05-13 15:42:04 -0400825 : R.plurals.zen_mode_duration_minutes;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400826 line1 = res.getQuantityString(line1ResId, num, num, formattedTime);
827 line2 = res.getString(R.string.zen_mode_until, formattedTime);
828 } else if (minutes < DAY_MINUTES) {
John Spurlockc90e6fe2014-10-28 11:21:42 -0400829 // display as hours
830 num = Math.round(minutes / 60f);
Julia Reynolds0842fe82015-10-01 14:34:22 -0400831 int summaryResId = shortVersion ? R.plurals.zen_mode_duration_hours_summary_short
John Spurlockbbde2672015-05-13 15:42:04 -0400832 : R.plurals.zen_mode_duration_hours_summary;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400833 summary = res.getQuantityString(summaryResId, num, num, formattedTime);
834 int line1ResId = shortVersion ? R.plurals.zen_mode_duration_hours_short
John Spurlockbbde2672015-05-13 15:42:04 -0400835 : R.plurals.zen_mode_duration_hours;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400836 line1 = res.getQuantityString(line1ResId, num, num, formattedTime);
837 line2 = res.getString(R.string.zen_mode_until, formattedTime);
838 } else {
839 // display as day/time
840 summary = line1 = line2 = res.getString(R.string.zen_mode_until, formattedTime);
John Spurlockc90e6fe2014-10-28 11:21:42 -0400841 }
Julia Reynolds7635ab02017-07-25 08:34:44 -0400842 final Uri id = toCountdownConditionId(time, false);
John Spurlockc90e6fe2014-10-28 11:21:42 -0400843 return new Condition(id, summary, line1, line2, 0, Condition.STATE_TRUE,
Jason Monk4dd81462014-09-08 10:13:59 -0400844 Condition.FLAG_RELEVANT_NOW);
845 }
846
Julia Reynolds7635ab02017-07-25 08:34:44 -0400847 /**
848 * Converts countdown to alarm parameters into a condition with user facing summary
849 */
850 public static Condition toNextAlarmCondition(Context context, long alarm,
Julia Reynolds0842fe82015-10-01 14:34:22 -0400851 int userHandle) {
Julia Reynolds7635ab02017-07-25 08:34:44 -0400852 boolean isSameDay = isToday(alarm);
853 final CharSequence formattedTime = getFormattedTime(context, alarm, isSameDay, userHandle);
Julia Reynolds0842fe82015-10-01 14:34:22 -0400854 final Resources res = context.getResources();
Julia Reynolds7635ab02017-07-25 08:34:44 -0400855 final String line1 = res.getString(R.string.zen_mode_until, formattedTime);
856 final Uri id = toCountdownConditionId(alarm, true);
Julia Reynolds0842fe82015-10-01 14:34:22 -0400857 return new Condition(id, "", line1, "", 0, Condition.STATE_TRUE,
858 Condition.FLAG_RELEVANT_NOW);
859 }
860
Beverlybe6d3522017-11-20 11:01:59 -0500861 /**
862 * Creates readable time from time in milliseconds
863 */
864 public static CharSequence getFormattedTime(Context context, long time, boolean isSameDay,
Julia Reynolds7635ab02017-07-25 08:34:44 -0400865 int userHandle) {
866 String skeleton = (!isSameDay ? "EEE " : "")
867 + (DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma");
868 final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
869 return DateFormat.format(pattern, time);
870 }
871
Beverlybe6d3522017-11-20 11:01:59 -0500872 /**
873 * Determines whether a time in milliseconds is today or not
874 */
875 public static boolean isToday(long time) {
Julia Reynolds8e2d2bb2015-10-01 14:34:22 -0400876 GregorianCalendar now = new GregorianCalendar();
877 GregorianCalendar endTime = new GregorianCalendar();
878 endTime.setTimeInMillis(time);
879 if (now.get(Calendar.YEAR) == endTime.get(Calendar.YEAR)
880 && now.get(Calendar.MONTH) == endTime.get(Calendar.MONTH)
881 && now.get(Calendar.DATE) == endTime.get(Calendar.DATE)) {
Julia Reynolds7635ab02017-07-25 08:34:44 -0400882 return true;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400883 }
Julia Reynolds7635ab02017-07-25 08:34:44 -0400884 return false;
Julia Reynolds0842fe82015-10-01 14:34:22 -0400885 }
886
John Spurlockd60258f2015-04-30 09:30:52 -0400887 // ==== Built-in system conditions ====
888
John Spurlock37bc92c2014-11-03 11:01:51 -0500889 public static final String SYSTEM_AUTHORITY = "android";
John Spurlock4db0d982014-08-13 09:19:03 -0400890
John Spurlockd60258f2015-04-30 09:30:52 -0400891 // ==== Built-in system condition: countdown ====
892
John Spurlock530052a2014-11-30 16:26:19 -0500893 public static final String COUNTDOWN_PATH = "countdown";
John Spurlock856edeb2014-06-01 20:36:47 -0400894
Julia Reynolds7635ab02017-07-25 08:34:44 -0400895 public static final String IS_ALARM_PATH = "alarm";
896
897 /**
898 * Converts countdown condition parameters into a condition id.
899 */
900 public static Uri toCountdownConditionId(long time, boolean alarm) {
John Spurlock856edeb2014-06-01 20:36:47 -0400901 return new Uri.Builder().scheme(Condition.SCHEME)
John Spurlock4db0d982014-08-13 09:19:03 -0400902 .authority(SYSTEM_AUTHORITY)
John Spurlock856edeb2014-06-01 20:36:47 -0400903 .appendPath(COUNTDOWN_PATH)
904 .appendPath(Long.toString(time))
Julia Reynolds7635ab02017-07-25 08:34:44 -0400905 .appendPath(IS_ALARM_PATH)
906 .appendPath(Boolean.toString(alarm))
John Spurlock856edeb2014-06-01 20:36:47 -0400907 .build();
908 }
909
910 public static long tryParseCountdownConditionId(Uri conditionId) {
John Spurlock4db0d982014-08-13 09:19:03 -0400911 if (!Condition.isValidId(conditionId, SYSTEM_AUTHORITY)) return 0;
Julia Reynolds7635ab02017-07-25 08:34:44 -0400912 if (conditionId.getPathSegments().size() < 2
John Spurlock856edeb2014-06-01 20:36:47 -0400913 || !COUNTDOWN_PATH.equals(conditionId.getPathSegments().get(0))) return 0;
914 try {
915 return Long.parseLong(conditionId.getPathSegments().get(1));
916 } catch (RuntimeException e) {
917 Slog.w(TAG, "Error parsing countdown condition: " + conditionId, e);
918 return 0;
919 }
920 }
921
Julia Reynolds7635ab02017-07-25 08:34:44 -0400922 /**
923 * Returns whether this condition is a countdown condition.
924 */
John Spurlock856edeb2014-06-01 20:36:47 -0400925 public static boolean isValidCountdownConditionId(Uri conditionId) {
926 return tryParseCountdownConditionId(conditionId) != 0;
927 }
John Spurlock4db0d982014-08-13 09:19:03 -0400928
Julia Reynolds7635ab02017-07-25 08:34:44 -0400929 /**
930 * Returns whether this condition is a countdown to an alarm.
931 */
932 public static boolean isValidCountdownToAlarmConditionId(Uri conditionId) {
933 if (tryParseCountdownConditionId(conditionId) != 0) {
934 if (conditionId.getPathSegments().size() < 4
935 || !IS_ALARM_PATH.equals(conditionId.getPathSegments().get(2))) {
936 return false;
937 }
938 try {
939 return Boolean.parseBoolean(conditionId.getPathSegments().get(3));
940 } catch (RuntimeException e) {
941 Slog.w(TAG, "Error parsing countdown alarm condition: " + conditionId, e);
942 return false;
943 }
944 }
945 return false;
946 }
947
John Spurlockd60258f2015-04-30 09:30:52 -0400948 // ==== Built-in system condition: schedule ====
949
John Spurlockb2278d62015-04-07 12:47:12 -0400950 public static final String SCHEDULE_PATH = "schedule";
John Spurlock4db0d982014-08-13 09:19:03 -0400951
John Spurlockd60258f2015-04-30 09:30:52 -0400952 public static Uri toScheduleConditionId(ScheduleInfo schedule) {
953 return new Uri.Builder().scheme(Condition.SCHEME)
954 .authority(SYSTEM_AUTHORITY)
955 .appendPath(SCHEDULE_PATH)
956 .appendQueryParameter("days", toDayList(schedule.days))
957 .appendQueryParameter("start", schedule.startHour + "." + schedule.startMinute)
958 .appendQueryParameter("end", schedule.endHour + "." + schedule.endMinute)
Julia Reynolds1998ee52016-02-11 13:49:08 -0500959 .appendQueryParameter("exitAtAlarm", String.valueOf(schedule.exitAtAlarm))
John Spurlockd60258f2015-04-30 09:30:52 -0400960 .build();
961 }
962
963 public static boolean isValidScheduleConditionId(Uri conditionId) {
Julia Reynolds4b318432017-11-22 10:56:18 -0500964 ScheduleInfo info;
965 try {
966 info = tryParseScheduleConditionId(conditionId);
967 } catch (NullPointerException | ArrayIndexOutOfBoundsException e) {
968 return false;
969 }
970
971 if (info == null || info.days == null || info.days.length == 0) {
972 return false;
973 }
974 return true;
John Spurlockd60258f2015-04-30 09:30:52 -0400975 }
976
977 public static ScheduleInfo tryParseScheduleConditionId(Uri conditionId) {
978 final boolean isSchedule = conditionId != null
979 && conditionId.getScheme().equals(Condition.SCHEME)
980 && conditionId.getAuthority().equals(ZenModeConfig.SYSTEM_AUTHORITY)
981 && conditionId.getPathSegments().size() == 1
982 && conditionId.getPathSegments().get(0).equals(ZenModeConfig.SCHEDULE_PATH);
983 if (!isSchedule) return null;
984 final int[] start = tryParseHourAndMinute(conditionId.getQueryParameter("start"));
985 final int[] end = tryParseHourAndMinute(conditionId.getQueryParameter("end"));
986 if (start == null || end == null) return null;
987 final ScheduleInfo rt = new ScheduleInfo();
988 rt.days = tryParseDayList(conditionId.getQueryParameter("days"), "\\.");
989 rt.startHour = start[0];
990 rt.startMinute = start[1];
991 rt.endHour = end[0];
992 rt.endMinute = end[1];
Julia Reynolds1998ee52016-02-11 13:49:08 -0500993 rt.exitAtAlarm = safeBoolean(conditionId.getQueryParameter("exitAtAlarm"), false);
John Spurlockd60258f2015-04-30 09:30:52 -0400994 return rt;
995 }
996
Julia Reynoldsa47a27f2015-08-24 08:31:47 -0400997 public static ComponentName getScheduleConditionProvider() {
998 return new ComponentName(SYSTEM_AUTHORITY, "ScheduleConditionProvider");
999 }
1000
John Spurlockb2278d62015-04-07 12:47:12 -04001001 public static class ScheduleInfo {
1002 public int[] days;
1003 public int startHour;
1004 public int startMinute;
1005 public int endHour;
1006 public int endMinute;
Julia Reynolds1998ee52016-02-11 13:49:08 -05001007 public boolean exitAtAlarm;
1008 public long nextAlarm;
John Spurlockb2278d62015-04-07 12:47:12 -04001009
1010 @Override
1011 public int hashCode() {
1012 return 0;
1013 }
1014
1015 @Override
1016 public boolean equals(Object o) {
1017 if (!(o instanceof ScheduleInfo)) return false;
1018 final ScheduleInfo other = (ScheduleInfo) o;
1019 return toDayList(days).equals(toDayList(other.days))
1020 && startHour == other.startHour
1021 && startMinute == other.startMinute
1022 && endHour == other.endHour
Julia Reynolds1998ee52016-02-11 13:49:08 -05001023 && endMinute == other.endMinute
1024 && exitAtAlarm == other.exitAtAlarm;
John Spurlockb2278d62015-04-07 12:47:12 -04001025 }
1026
1027 public ScheduleInfo copy() {
1028 final ScheduleInfo rt = new ScheduleInfo();
1029 if (days != null) {
1030 rt.days = new int[days.length];
1031 System.arraycopy(days, 0, rt.days, 0, days.length);
1032 }
1033 rt.startHour = startHour;
1034 rt.startMinute = startMinute;
1035 rt.endHour = endHour;
1036 rt.endMinute = endMinute;
Julia Reynolds1998ee52016-02-11 13:49:08 -05001037 rt.exitAtAlarm = exitAtAlarm;
1038 rt.nextAlarm = nextAlarm;
John Spurlockb2278d62015-04-07 12:47:12 -04001039 return rt;
1040 }
Julia Reynolds7f733082016-04-26 14:19:19 -04001041
1042 @Override
1043 public String toString() {
1044 return "ScheduleInfo{" +
1045 "days=" + Arrays.toString(days) +
1046 ", startHour=" + startHour +
1047 ", startMinute=" + startMinute +
1048 ", endHour=" + endHour +
1049 ", endMinute=" + endMinute +
1050 ", exitAtAlarm=" + exitAtAlarm +
Julia Reynoldsfe58f1f2016-07-19 10:18:32 -04001051 ", nextAlarm=" + ts(nextAlarm) +
Julia Reynolds7f733082016-04-26 14:19:19 -04001052 '}';
1053 }
Julia Reynoldsfe58f1f2016-07-19 10:18:32 -04001054
1055 protected static String ts(long time) {
1056 return new Date(time) + " (" + time + ")";
1057 }
John Spurlockb2278d62015-04-07 12:47:12 -04001058 }
1059
John Spurlockd60258f2015-04-30 09:30:52 -04001060 // ==== Built-in system condition: event ====
1061
1062 public static final String EVENT_PATH = "event";
1063
1064 public static Uri toEventConditionId(EventInfo event) {
John Spurlock4db0d982014-08-13 09:19:03 -04001065 return new Uri.Builder().scheme(Condition.SCHEME)
1066 .authority(SYSTEM_AUTHORITY)
John Spurlockd60258f2015-04-30 09:30:52 -04001067 .appendPath(EVENT_PATH)
John Spurlock1b8b22b2015-05-20 09:47:13 -04001068 .appendQueryParameter("userId", Long.toString(event.userId))
John Spurlock995a7492015-05-28 22:13:03 -04001069 .appendQueryParameter("calendar", event.calendar != null ? event.calendar : "")
John Spurlockd60258f2015-04-30 09:30:52 -04001070 .appendQueryParameter("reply", Integer.toString(event.reply))
John Spurlock4db0d982014-08-13 09:19:03 -04001071 .build();
1072 }
1073
John Spurlockd60258f2015-04-30 09:30:52 -04001074 public static boolean isValidEventConditionId(Uri conditionId) {
1075 return tryParseEventConditionId(conditionId) != null;
John Spurlockb2278d62015-04-07 12:47:12 -04001076 }
1077
John Spurlockd60258f2015-04-30 09:30:52 -04001078 public static EventInfo tryParseEventConditionId(Uri conditionId) {
1079 final boolean isEvent = conditionId != null
John Spurlockb2278d62015-04-07 12:47:12 -04001080 && conditionId.getScheme().equals(Condition.SCHEME)
1081 && conditionId.getAuthority().equals(ZenModeConfig.SYSTEM_AUTHORITY)
1082 && conditionId.getPathSegments().size() == 1
John Spurlockd60258f2015-04-30 09:30:52 -04001083 && conditionId.getPathSegments().get(0).equals(EVENT_PATH);
1084 if (!isEvent) return null;
1085 final EventInfo rt = new EventInfo();
John Spurlock1b8b22b2015-05-20 09:47:13 -04001086 rt.userId = tryParseInt(conditionId.getQueryParameter("userId"), UserHandle.USER_NULL);
John Spurlock995a7492015-05-28 22:13:03 -04001087 rt.calendar = conditionId.getQueryParameter("calendar");
1088 if (TextUtils.isEmpty(rt.calendar) || tryParseLong(rt.calendar, -1L) != -1L) {
1089 rt.calendar = null;
1090 }
John Spurlockd60258f2015-04-30 09:30:52 -04001091 rt.reply = tryParseInt(conditionId.getQueryParameter("reply"), 0);
John Spurlockb2278d62015-04-07 12:47:12 -04001092 return rt;
John Spurlock4db0d982014-08-13 09:19:03 -04001093 }
1094
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001095 public static ComponentName getEventConditionProvider() {
1096 return new ComponentName(SYSTEM_AUTHORITY, "EventConditionProvider");
1097 }
1098
John Spurlockd60258f2015-04-30 09:30:52 -04001099 public static class EventInfo {
John Spurlockd39af2d2015-05-05 09:49:32 -04001100 public static final int REPLY_ANY_EXCEPT_NO = 0;
1101 public static final int REPLY_YES_OR_MAYBE = 1;
John Spurlockd60258f2015-04-30 09:30:52 -04001102 public static final int REPLY_YES = 2;
1103
John Spurlock1b8b22b2015-05-20 09:47:13 -04001104 public int userId = UserHandle.USER_NULL; // USER_NULL = unspecified - use current user
John Spurlock995a7492015-05-28 22:13:03 -04001105 public String calendar; // CalendarContract.Calendars.OWNER_ACCOUNT, or null for any
John Spurlockd60258f2015-04-30 09:30:52 -04001106 public int reply;
1107
1108 @Override
1109 public int hashCode() {
1110 return 0;
1111 }
1112
1113 @Override
1114 public boolean equals(Object o) {
1115 if (!(o instanceof EventInfo)) return false;
1116 final EventInfo other = (EventInfo) o;
John Spurlock1b8b22b2015-05-20 09:47:13 -04001117 return userId == other.userId
John Spurlock995a7492015-05-28 22:13:03 -04001118 && Objects.equals(calendar, other.calendar)
John Spurlockd60258f2015-04-30 09:30:52 -04001119 && reply == other.reply;
1120 }
1121
1122 public EventInfo copy() {
1123 final EventInfo rt = new EventInfo();
John Spurlock1b8b22b2015-05-20 09:47:13 -04001124 rt.userId = userId;
John Spurlockd60258f2015-04-30 09:30:52 -04001125 rt.calendar = calendar;
John Spurlockd60258f2015-04-30 09:30:52 -04001126 rt.reply = reply;
1127 return rt;
1128 }
John Spurlock1b8b22b2015-05-20 09:47:13 -04001129
1130 public static int resolveUserId(int userId) {
1131 return userId == UserHandle.USER_NULL ? ActivityManager.getCurrentUser() : userId;
John Spurlock1b8b22b2015-05-20 09:47:13 -04001132 }
John Spurlockd60258f2015-04-30 09:30:52 -04001133 }
1134
1135 // ==== End built-in system conditions ====
1136
John Spurlock4db0d982014-08-13 09:19:03 -04001137 private static int[] tryParseHourAndMinute(String value) {
1138 if (TextUtils.isEmpty(value)) return null;
1139 final int i = value.indexOf('.');
1140 if (i < 1 || i >= value.length() - 1) return null;
1141 final int hour = tryParseInt(value.substring(0, i), -1);
1142 final int minute = tryParseInt(value.substring(i + 1), -1);
1143 return isValidHour(hour) && isValidMinute(minute) ? new int[] { hour, minute } : null;
1144 }
1145
John Spurlockb2278d62015-04-07 12:47:12 -04001146 private static int tryParseZenMode(String value, int defValue) {
1147 final int rt = tryParseInt(value, defValue);
1148 return Global.isValidZenMode(rt) ? rt : defValue;
John Spurlock4db0d982014-08-13 09:19:03 -04001149 }
1150
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001151 public static String newRuleId() {
John Spurlockb2278d62015-04-07 12:47:12 -04001152 return UUID.randomUUID().toString().replace("-", "");
1153 }
1154
Beverlybe6d3522017-11-20 11:01:59 -05001155 /**
1156 * Gets the name of the app associated with owner
1157 */
1158 public static String getOwnerCaption(Context context, String owner) {
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001159 final PackageManager pm = context.getPackageManager();
1160 try {
1161 final ApplicationInfo info = pm.getApplicationInfo(owner, 0);
1162 if (info != null) {
1163 final CharSequence seq = info.loadLabel(pm);
1164 if (seq != null) {
1165 final String str = seq.toString().trim();
1166 if (str.length() > 0) {
1167 return str;
1168 }
1169 }
1170 }
1171 } catch (Throwable e) {
1172 Slog.w(TAG, "Error loading owner caption", e);
1173 }
1174 return "";
1175 }
1176
John Spurlockb2278d62015-04-07 12:47:12 -04001177 public static String getConditionSummary(Context context, ZenModeConfig config,
John Spurlockbbde2672015-05-13 15:42:04 -04001178 int userHandle, boolean shortVersion) {
1179 return getConditionLine(context, config, userHandle, false /*useLine1*/, shortVersion);
John Spurlockb2278d62015-04-07 12:47:12 -04001180 }
1181
1182 private static String getConditionLine(Context context, ZenModeConfig config,
John Spurlockbbde2672015-05-13 15:42:04 -04001183 int userHandle, boolean useLine1, boolean shortVersion) {
John Spurlockb2278d62015-04-07 12:47:12 -04001184 if (config == null) return "";
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001185 String summary = "";
John Spurlockb2278d62015-04-07 12:47:12 -04001186 if (config.manualRule != null) {
1187 final Uri id = config.manualRule.conditionId;
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001188 if (config.manualRule.enabler != null) {
1189 summary = getOwnerCaption(context, config.manualRule.enabler);
1190 } else {
1191 if (id == null) {
1192 summary = context.getString(com.android.internal.R.string.zen_mode_forever);
1193 } else {
1194 final long time = tryParseCountdownConditionId(id);
1195 Condition c = config.manualRule.condition;
1196 if (time > 0) {
1197 final long now = System.currentTimeMillis();
1198 final long span = time - now;
1199 c = toTimeCondition(context, time, Math.round(span / (float) MINUTES_MS),
1200 userHandle, shortVersion);
1201 }
1202 final String rt = c == null ? "" : useLine1 ? c.line1 : c.summary;
1203 summary = TextUtils.isEmpty(rt) ? "" : rt;
1204 }
John Spurlockb2278d62015-04-07 12:47:12 -04001205 }
John Spurlockb2278d62015-04-07 12:47:12 -04001206 }
John Spurlockb2278d62015-04-07 12:47:12 -04001207 for (ZenRule automaticRule : config.automaticRules.values()) {
John Spurlock39581cc2015-04-10 11:59:01 -04001208 if (automaticRule.isAutomaticActive()) {
John Spurlockb2278d62015-04-07 12:47:12 -04001209 if (summary.isEmpty()) {
1210 summary = automaticRule.name;
1211 } else {
1212 summary = context.getResources()
1213 .getString(R.string.zen_mode_rule_name_combination, summary,
1214 automaticRule.name);
1215 }
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001216
John Spurlockb2278d62015-04-07 12:47:12 -04001217 }
1218 }
1219 return summary;
1220 }
1221
1222 public static class ZenRule implements Parcelable {
1223 public boolean enabled;
1224 public boolean snoozing; // user manually disabled this instance
Julia Reynolds361e82d32016-02-26 18:19:49 -05001225 public String name; // required for automatic
John Spurlockb2278d62015-04-07 12:47:12 -04001226 public int zenMode;
1227 public Uri conditionId; // required for automatic
1228 public Condition condition; // optional
1229 public ComponentName component; // optional
Julia Reynolds56106ff2015-09-30 14:42:53 -04001230 public String id; // required for automatic (unique)
1231 public long creationTime; // required for automatic
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001232 public String enabler; // package name, only used for manual rules.
John Spurlockb2278d62015-04-07 12:47:12 -04001233
1234 public ZenRule() { }
1235
1236 public ZenRule(Parcel source) {
1237 enabled = source.readInt() == 1;
1238 snoozing = source.readInt() == 1;
1239 if (source.readInt() == 1) {
1240 name = source.readString();
1241 }
1242 zenMode = source.readInt();
1243 conditionId = source.readParcelable(null);
1244 condition = source.readParcelable(null);
1245 component = source.readParcelable(null);
Julia Reynolds56106ff2015-09-30 14:42:53 -04001246 if (source.readInt() == 1) {
1247 id = source.readString();
1248 }
1249 creationTime = source.readLong();
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001250 if (source.readInt() == 1) {
1251 enabler = source.readString();
1252 }
John Spurlockb2278d62015-04-07 12:47:12 -04001253 }
John Spurlock4db0d982014-08-13 09:19:03 -04001254
1255 @Override
John Spurlockb2278d62015-04-07 12:47:12 -04001256 public int describeContents() {
John Spurlock4db0d982014-08-13 09:19:03 -04001257 return 0;
1258 }
1259
1260 @Override
John Spurlockb2278d62015-04-07 12:47:12 -04001261 public void writeToParcel(Parcel dest, int flags) {
1262 dest.writeInt(enabled ? 1 : 0);
1263 dest.writeInt(snoozing ? 1 : 0);
1264 if (name != null) {
1265 dest.writeInt(1);
1266 dest.writeString(name);
1267 } else {
1268 dest.writeInt(0);
1269 }
1270 dest.writeInt(zenMode);
1271 dest.writeParcelable(conditionId, 0);
1272 dest.writeParcelable(condition, 0);
1273 dest.writeParcelable(component, 0);
Julia Reynolds56106ff2015-09-30 14:42:53 -04001274 if (id != null) {
1275 dest.writeInt(1);
1276 dest.writeString(id);
1277 } else {
1278 dest.writeInt(0);
1279 }
1280 dest.writeLong(creationTime);
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001281 if (enabler != null) {
1282 dest.writeInt(1);
1283 dest.writeString(enabler);
1284 } else {
1285 dest.writeInt(0);
1286 }
John Spurlockb2278d62015-04-07 12:47:12 -04001287 }
1288
1289 @Override
1290 public String toString() {
1291 return new StringBuilder(ZenRule.class.getSimpleName()).append('[')
1292 .append("enabled=").append(enabled)
1293 .append(",snoozing=").append(snoozing)
1294 .append(",name=").append(name)
1295 .append(",zenMode=").append(Global.zenModeToString(zenMode))
1296 .append(",conditionId=").append(conditionId)
1297 .append(",condition=").append(condition)
1298 .append(",component=").append(component)
Julia Reynolds56106ff2015-09-30 14:42:53 -04001299 .append(",id=").append(id)
1300 .append(",creationTime=").append(creationTime)
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001301 .append(",enabler=").append(enabler)
John Spurlockb2278d62015-04-07 12:47:12 -04001302 .append(']').toString();
1303 }
1304
Kweku Adams99546332018-01-24 17:03:50 -08001305 /** @hide */
1306 public void writeToProto(ProtoOutputStream proto, long fieldId) {
1307 final long token = proto.start(fieldId);
1308
1309 proto.write(ZenRuleProto.ID, id);
1310 proto.write(ZenRuleProto.NAME, name);
1311 proto.write(ZenRuleProto.CREATION_TIME_MS, creationTime);
1312 proto.write(ZenRuleProto.ENABLED, enabled);
1313 proto.write(ZenRuleProto.ENABLER, enabler);
1314 proto.write(ZenRuleProto.IS_SNOOZING, snoozing);
1315 proto.write(ZenRuleProto.ZEN_MODE, zenMode);
1316 if (conditionId != null) {
1317 proto.write(ZenRuleProto.CONDITION_ID, conditionId.toString());
1318 }
1319 if (condition != null) {
1320 condition.writeToProto(proto, ZenRuleProto.CONDITION);
1321 }
1322 if (component != null) {
1323 component.writeToProto(proto, ZenRuleProto.COMPONENT);
1324 }
1325
1326 proto.end(token);
1327 }
1328
John Spurlock21258a32015-05-27 18:22:55 -04001329 private static void appendDiff(Diff d, String item, ZenRule from, ZenRule to) {
1330 if (d == null) return;
1331 if (from == null) {
1332 if (to != null) {
1333 d.addLine(item, "insert");
1334 }
1335 return;
1336 }
1337 from.appendDiff(d, item, to);
1338 }
1339
1340 private void appendDiff(Diff d, String item, ZenRule to) {
1341 if (to == null) {
1342 d.addLine(item, "delete");
1343 return;
1344 }
1345 if (enabled != to.enabled) {
1346 d.addLine(item, "enabled", enabled, to.enabled);
1347 }
1348 if (snoozing != to.snoozing) {
1349 d.addLine(item, "snoozing", snoozing, to.snoozing);
1350 }
1351 if (!Objects.equals(name, to.name)) {
1352 d.addLine(item, "name", name, to.name);
1353 }
1354 if (zenMode != to.zenMode) {
1355 d.addLine(item, "zenMode", zenMode, to.zenMode);
1356 }
1357 if (!Objects.equals(conditionId, to.conditionId)) {
1358 d.addLine(item, "conditionId", conditionId, to.conditionId);
1359 }
1360 if (!Objects.equals(condition, to.condition)) {
1361 d.addLine(item, "condition", condition, to.condition);
1362 }
1363 if (!Objects.equals(component, to.component)) {
1364 d.addLine(item, "component", component, to.component);
1365 }
Julia Reynolds56106ff2015-09-30 14:42:53 -04001366 if (!Objects.equals(id, to.id)) {
1367 d.addLine(item, "id", id, to.id);
1368 }
Julia Reynoldsc8e54e82015-11-30 16:43:05 -05001369 if (creationTime != to.creationTime) {
Julia Reynolds56106ff2015-09-30 14:42:53 -04001370 d.addLine(item, "creationTime", creationTime, to.creationTime);
1371 }
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001372 if (enabler != to.enabler) {
1373 d.addLine(item, "enabler", enabler, to.enabler);
1374 }
John Spurlock21258a32015-05-27 18:22:55 -04001375 }
1376
John Spurlockb2278d62015-04-07 12:47:12 -04001377 @Override
John Spurlock4db0d982014-08-13 09:19:03 -04001378 public boolean equals(Object o) {
John Spurlockb2278d62015-04-07 12:47:12 -04001379 if (!(o instanceof ZenRule)) return false;
1380 if (o == this) return true;
1381 final ZenRule other = (ZenRule) o;
1382 return other.enabled == enabled
1383 && other.snoozing == snoozing
1384 && Objects.equals(other.name, name)
1385 && other.zenMode == zenMode
1386 && Objects.equals(other.conditionId, conditionId)
1387 && Objects.equals(other.condition, condition)
Julia Reynolds56106ff2015-09-30 14:42:53 -04001388 && Objects.equals(other.component, component)
1389 && Objects.equals(other.id, id)
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001390 && other.creationTime == creationTime
1391 && Objects.equals(other.enabler, enabler);
John Spurlockb2278d62015-04-07 12:47:12 -04001392 }
1393
1394 @Override
1395 public int hashCode() {
1396 return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001397 component, id, creationTime, enabler);
John Spurlockb2278d62015-04-07 12:47:12 -04001398 }
1399
John Spurlock39581cc2015-04-10 11:59:01 -04001400 public boolean isAutomaticActive() {
1401 return enabled && !snoozing && component != null && isTrueOrUnknown();
1402 }
1403
John Spurlockb2278d62015-04-07 12:47:12 -04001404 public boolean isTrueOrUnknown() {
John Spurlock39581cc2015-04-10 11:59:01 -04001405 return condition != null && (condition.state == Condition.STATE_TRUE
1406 || condition.state == Condition.STATE_UNKNOWN);
John Spurlockb2278d62015-04-07 12:47:12 -04001407 }
1408
1409 public static final Parcelable.Creator<ZenRule> CREATOR
1410 = new Parcelable.Creator<ZenRule>() {
1411 @Override
1412 public ZenRule createFromParcel(Parcel source) {
1413 return new ZenRule(source);
1414 }
1415 @Override
1416 public ZenRule[] newArray(int size) {
1417 return new ZenRule[size];
1418 }
1419 };
1420 }
1421
John Spurlock21258a32015-05-27 18:22:55 -04001422 public static class Diff {
1423 private final ArrayList<String> lines = new ArrayList<>();
1424
1425 @Override
1426 public String toString() {
1427 final StringBuilder sb = new StringBuilder("Diff[");
1428 final int N = lines.size();
1429 for (int i = 0; i < N; i++) {
1430 if (i > 0) {
1431 sb.append(',');
1432 }
1433 sb.append(lines.get(i));
1434 }
1435 return sb.append(']').toString();
1436 }
1437
1438 private Diff addLine(String item, String action) {
1439 lines.add(item + ":" + action);
1440 return this;
1441 }
1442
1443 public Diff addLine(String item, String subitem, Object from, Object to) {
1444 return addLine(item + "." + subitem, from, to);
1445 }
1446
1447 public Diff addLine(String item, Object from, Object to) {
1448 return addLine(item, from + "->" + to);
1449 }
1450 }
1451
Beverly925cde82018-01-23 09:31:23 -05001452 /**
Beverlyd6964762018-02-16 14:07:03 -05001453 * Determines whether dnd behavior should mute all notification/ringer sounds
1454 * (sounds associated with ringer volume discluding system)
Beverly925cde82018-01-23 09:31:23 -05001455 */
1456 public static boolean areAllPriorityOnlyNotificationZenSoundsMuted(NotificationManager.Policy
1457 policy) {
1458 boolean allowReminders = (policy.priorityCategories
1459 & NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS) != 0;
1460 boolean allowCalls = (policy.priorityCategories
1461 & NotificationManager.Policy.PRIORITY_CATEGORY_CALLS) != 0;
1462 boolean allowMessages = (policy.priorityCategories
1463 & NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) != 0;
1464 boolean allowEvents = (policy.priorityCategories
1465 & NotificationManager.Policy.PRIORITY_CATEGORY_EVENTS) != 0;
1466 boolean allowRepeatCallers = (policy.priorityCategories
1467 & NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS) != 0;
1468 return !allowReminders && !allowCalls && !allowMessages && !allowEvents
1469 && !allowRepeatCallers;
1470 }
1471
1472 /**
Beverlyd6964762018-02-16 14:07:03 -05001473 * Determines whether dnd behavior should mute all sounds controlled by ringer
Beverly925cde82018-01-23 09:31:23 -05001474 */
1475 public static boolean areAllPriorityOnlyNotificationZenSoundsMuted(ZenModeConfig config) {
1476 return !config.allowReminders && !config.allowCalls && !config.allowMessages
1477 && !config.allowEvents && !config.allowRepeatCallers;
1478 }
1479
1480 /**
1481 * Determines whether all dnd mutes all sounds
1482 */
1483 public static boolean areAllZenBehaviorSoundsMuted(ZenModeConfig config) {
Beverlyd6964762018-02-16 14:07:03 -05001484 return !config.allowAlarms && !config.allowMedia && !config.allowSystem
Beverly925cde82018-01-23 09:31:23 -05001485 && areAllPriorityOnlyNotificationZenSoundsMuted(config);
1486 }
Beverlya5effb42018-02-01 17:08:35 -05001487
1488 /**
1489 * Returns a description of the current do not disturb settings from config.
1490 * - If turned on manually and end time is known, returns end time.
Beverlyebcaeeb2018-04-06 14:15:23 -04001491 * - If turned on manually and end time is on forever until turned off, return null if
1492 * describeForeverCondition is false, else return String describing indefinite behavior
Beverlya5effb42018-02-01 17:08:35 -05001493 * - If turned on by an automatic rule, returns the automatic rule name.
1494 * - If on due to an app, returns the app name.
1495 * - If there's a combination of rules/apps that trigger, then shows the one that will
1496 * last the longest if applicable.
Beverlyebcaeeb2018-04-06 14:15:23 -04001497 * @return null if DND is off or describeForeverCondition is false and
1498 * DND is on forever (until turned off)
Beverlya5effb42018-02-01 17:08:35 -05001499 */
Beverlyebcaeeb2018-04-06 14:15:23 -04001500 public static String getDescription(Context context, boolean zenOn, ZenModeConfig config,
1501 boolean describeForeverCondition) {
1502 if (!zenOn || config == null) {
Beverlya5effb42018-02-01 17:08:35 -05001503 return null;
1504 }
1505
1506 String secondaryText = "";
1507 long latestEndTime = -1;
1508
1509 // DND turned on by manual rule
1510 if (config.manualRule != null) {
1511 final Uri id = config.manualRule.conditionId;
1512 if (config.manualRule.enabler != null) {
1513 // app triggered manual rule
1514 String appName = getOwnerCaption(context, config.manualRule.enabler);
1515 if (!appName.isEmpty()) {
1516 secondaryText = appName;
1517 }
1518 } else {
1519 if (id == null) {
1520 // Do not disturb manually triggered to remain on forever until turned off
Beverlyebcaeeb2018-04-06 14:15:23 -04001521 if (describeForeverCondition) {
1522 return context.getString(R.string.zen_mode_forever);
1523 } else {
1524 return null;
1525 }
Beverlya5effb42018-02-01 17:08:35 -05001526 } else {
1527 latestEndTime = tryParseCountdownConditionId(id);
1528 if (latestEndTime > 0) {
1529 final CharSequence formattedTime = getFormattedTime(context,
1530 latestEndTime, isToday(latestEndTime),
1531 context.getUserId());
1532 secondaryText = context.getString(R.string.zen_mode_until, formattedTime);
1533 }
1534 }
1535 }
1536 }
1537
1538 // DND turned on by an automatic rule
1539 for (ZenRule automaticRule : config.automaticRules.values()) {
1540 if (automaticRule.isAutomaticActive()) {
1541 if (isValidEventConditionId(automaticRule.conditionId)
1542 || isValidScheduleConditionId(automaticRule.conditionId)) {
1543 // set text if automatic rule end time is the latest active rule end time
1544 long endTime = parseAutomaticRuleEndTime(context, automaticRule.conditionId);
1545 if (endTime > latestEndTime) {
1546 latestEndTime = endTime;
1547 secondaryText = automaticRule.name;
1548 }
1549 } else {
1550 // set text if 3rd party rule
1551 return automaticRule.name;
1552 }
1553 }
1554 }
1555
1556 return !secondaryText.equals("") ? secondaryText : null;
1557 }
1558
1559 private static long parseAutomaticRuleEndTime(Context context, Uri id) {
1560 if (isValidEventConditionId(id)) {
1561 // cannot look up end times for events
1562 return Long.MAX_VALUE;
1563 }
1564
1565 if (isValidScheduleConditionId(id)) {
1566 ScheduleCalendar schedule = toScheduleCalendar(id);
1567 long endTimeMs = schedule.getNextChangeTime(System.currentTimeMillis());
1568
1569 // check if automatic rule will end on next alarm
1570 if (schedule.exitAtAlarm()) {
1571 long nextAlarm = getNextAlarm(context);
1572 schedule.maybeSetNextAlarm(System.currentTimeMillis(), nextAlarm);
1573 if (schedule.shouldExitForAlarm(endTimeMs)) {
1574 return nextAlarm;
1575 }
1576 }
1577
1578 return endTimeMs;
1579 }
1580
1581 return -1;
1582 }
1583
1584 private static long getNextAlarm(Context context) {
1585 final AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
1586 final AlarmManager.AlarmClockInfo info = alarms.getNextAlarmClock(context.getUserId());
1587 return info != null ? info.getTriggerTime() : 0;
1588 }
John Spurlock056c5192014-04-20 21:52:01 -04001589}