blob: cb7d41bdf21a2187246a0441e14e941f92e52833 [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
Mathew Inwoode3807372018-08-10 09:51:03 +010023import android.annotation.UnsupportedAppUsage;
John Spurlock1b8b22b2015-05-20 09:47:13 -040024import android.app.ActivityManager;
Beverlya5effb42018-02-01 17:08:35 -050025import android.app.AlarmManager;
Beverly925cde82018-01-23 09:31:23 -050026import android.app.NotificationManager;
John Spurlock1fc476d2015-04-14 16:05:20 -040027import android.app.NotificationManager.Policy;
John Spurlock3b98b3f2014-05-01 09:08:48 -040028import android.content.ComponentName;
John Spurlockc90e6fe2014-10-28 11:21:42 -040029import android.content.Context;
Julia Reynolds44ad6ff2016-07-06 09:47:45 -040030import android.content.pm.ApplicationInfo;
31import android.content.pm.PackageManager;
Jason Monk4dd81462014-09-08 10:13:59 -040032import android.content.res.Resources;
John Spurlock3b98b3f2014-05-01 09:08:48 -040033import android.net.Uri;
John Spurlock056c5192014-04-20 21:52:01 -040034import android.os.Parcel;
35import android.os.Parcelable;
John Spurlock1b8b22b2015-05-20 09:47:13 -040036import android.os.UserHandle;
John Spurlockb2278d62015-04-07 12:47:12 -040037import android.provider.Settings.Global;
John Spurlock056c5192014-04-20 21:52:01 -040038import android.text.TextUtils;
John Spurlockc90e6fe2014-10-28 11:21:42 -040039import android.text.format.DateFormat;
John Spurlockb2278d62015-04-07 12:47:12 -040040import android.util.ArrayMap;
41import android.util.ArraySet;
John Spurlock856edeb2014-06-01 20:36:47 -040042import android.util.Slog;
Kweku Adams99546332018-01-24 17:03:50 -080043import android.util.proto.ProtoOutputStream;
John Spurlock056c5192014-04-20 21:52:01 -040044
John Spurlockb2278d62015-04-07 12:47:12 -040045import com.android.internal.R;
46
John Spurlock056c5192014-04-20 21:52:01 -040047import org.xmlpull.v1.XmlPullParser;
48import org.xmlpull.v1.XmlPullParserException;
49import org.xmlpull.v1.XmlSerializer;
50
51import java.io.IOException;
John Spurlock3b98b3f2014-05-01 09:08:48 -040052import java.util.ArrayList;
Julia Reynolds7f733082016-04-26 14:19:19 -040053import java.util.Arrays;
John Spurlockae641c92014-06-30 18:11:40 -040054import java.util.Calendar;
Julia Reynoldsfe58f1f2016-07-19 10:18:32 -040055import java.util.Date;
Julia Reynolds8e2d2bb2015-10-01 14:34:22 -040056import java.util.GregorianCalendar;
Beverly26eba872017-12-04 16:30:11 -050057import java.util.List;
John Spurlockc90e6fe2014-10-28 11:21:42 -040058import java.util.Locale;
John Spurlock056c5192014-04-20 21:52:01 -040059import java.util.Objects;
Beverlybe6d3522017-11-20 11:01:59 -050060import java.util.TimeZone;
John Spurlockb2278d62015-04-07 12:47:12 -040061import java.util.UUID;
John Spurlockc90e6fe2014-10-28 11:21:42 -040062
John Spurlock056c5192014-04-20 21:52:01 -040063/**
64 * Persisted configuration for zen mode.
65 *
66 * @hide
67 */
68public class ZenModeConfig implements Parcelable {
John Spurlock856edeb2014-06-01 20:36:47 -040069 private static String TAG = "ZenModeConfig";
John Spurlock056c5192014-04-20 21:52:01 -040070
Beverlyff2df9b2018-10-10 16:54:10 -040071 public static final int SOURCE_ANYONE = Policy.PRIORITY_SENDERS_ANY;
72 public static final int SOURCE_CONTACT = Policy.PRIORITY_SENDERS_CONTACTS;
73 public static final int SOURCE_STAR = Policy.PRIORITY_SENDERS_STARRED;
Chris Wren99f963e2014-05-28 16:52:42 -040074 public static final int MAX_SOURCE = SOURCE_STAR;
John Spurlocka492d1d2015-05-05 18:30:28 -040075 private static final int DEFAULT_SOURCE = SOURCE_CONTACT;
Julia Reynolds1f580572018-04-27 14:48:36 -040076 private static final int DEFAULT_CALLS_SOURCE = SOURCE_STAR;
Chris Wren99f963e2014-05-28 16:52:42 -040077
Beverly26eba872017-12-04 16:30:11 -050078 public static final String EVENTS_DEFAULT_RULE_ID = "EVENTS_DEFAULT_RULE";
79 public static final String EVERY_NIGHT_DEFAULT_RULE_ID = "EVERY_NIGHT_DEFAULT_RULE";
80 public static final List<String> DEFAULT_RULE_IDS = Arrays.asList(EVERY_NIGHT_DEFAULT_RULE_ID,
81 EVENTS_DEFAULT_RULE_ID);
82
John Spurlockae641c92014-06-30 18:11:40 -040083 public static final int[] ALL_DAYS = { Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY,
84 Calendar.WEDNESDAY, Calendar.THURSDAY, Calendar.FRIDAY, Calendar.SATURDAY };
John Spurlockae641c92014-06-30 18:11:40 -040085
John Spurlockd9c75db2015-04-28 11:19:13 -040086 public static final int[] MINUTE_BUCKETS = generateMinuteBuckets();
Jason Monk4dd81462014-09-08 10:13:59 -040087 private static final int SECONDS_MS = 1000;
88 private static final int MINUTES_MS = 60 * SECONDS_MS;
Julia Reynolds0842fe82015-10-01 14:34:22 -040089 private static final int DAY_MINUTES = 24 * 60;
John Spurlock530052a2014-11-30 16:26:19 -050090 private static final int ZERO_VALUE_MS = 10 * SECONDS_MS;
Jason Monk4dd81462014-09-08 10:13:59 -040091
Julia Reynoldsc4e5ecf2018-04-11 11:33:32 -040092 // Default allow categories set in readXml() from default_zen_mode_config.xml,
93 // fallback/upgrade values:
Beverly04216872017-09-28 10:55:32 -040094 private static final boolean DEFAULT_ALLOW_ALARMS = true;
Beverlyd6964762018-02-16 14:07:03 -050095 private static final boolean DEFAULT_ALLOW_MEDIA = true;
96 private static final boolean DEFAULT_ALLOW_SYSTEM = false;
Julia Reynolds10a7cc12018-05-03 10:56:24 -040097 private static final boolean DEFAULT_ALLOW_CALLS = true;
John Spurlocka492d1d2015-05-05 18:30:28 -040098 private static final boolean DEFAULT_ALLOW_MESSAGES = false;
Beverly04216872017-09-28 10:55:32 -040099 private static final boolean DEFAULT_ALLOW_REMINDERS = false;
100 private static final boolean DEFAULT_ALLOW_EVENTS = false;
Julia Reynolds10a7cc12018-05-03 10:56:24 -0400101 private static final boolean DEFAULT_ALLOW_REPEAT_CALLERS = true;
Beverly86d076f2018-04-17 14:44:52 -0400102 private static final boolean DEFAULT_CHANNELS_BYPASSING_DND = false;
Julia Reynolds1f580572018-04-27 14:48:36 -0400103 private static final int DEFAULT_SUPPRESSED_VISUAL_EFFECTS = 0;
John Spurlock2dac62c2014-09-19 15:29:06 -0400104
Julia Reynolds1f580572018-04-27 14:48:36 -0400105 public static final int XML_VERSION = 8;
Julia Reynoldsd1bf5f02017-07-11 10:39:58 -0400106 public static final String ZEN_TAG = "zen";
John Spurlock056c5192014-04-20 21:52:01 -0400107 private static final String ZEN_ATT_VERSION = "version";
John Spurlock21258a32015-05-27 18:22:55 -0400108 private static final String ZEN_ATT_USER = "user";
John Spurlock056c5192014-04-20 21:52:01 -0400109 private static final String ALLOW_TAG = "allow";
Beverly04216872017-09-28 10:55:32 -0400110 private static final String ALLOW_ATT_ALARMS = "alarms";
Beverlyd6964762018-02-16 14:07:03 -0500111 private static final String ALLOW_ATT_MEDIA = "media";
112 private static final String ALLOW_ATT_SYSTEM = "system";
John Spurlock056c5192014-04-20 21:52:01 -0400113 private static final String ALLOW_ATT_CALLS = "calls";
John Spurlock1d7d2242015-04-10 08:10:22 -0400114 private static final String ALLOW_ATT_REPEAT_CALLERS = "repeatCallers";
John Spurlock056c5192014-04-20 21:52:01 -0400115 private static final String ALLOW_ATT_MESSAGES = "messages";
Chris Wren99f963e2014-05-28 16:52:42 -0400116 private static final String ALLOW_ATT_FROM = "from";
John Spurlocka492d1d2015-05-05 18:30:28 -0400117 private static final String ALLOW_ATT_CALLS_FROM = "callsFrom";
118 private static final String ALLOW_ATT_MESSAGES_FROM = "messagesFrom";
John Spurlockfc746f82015-04-03 13:47:14 -0400119 private static final String ALLOW_ATT_REMINDERS = "reminders";
John Spurlock2dac62c2014-09-19 15:29:06 -0400120 private static final String ALLOW_ATT_EVENTS = "events";
Julia Reynoldsd5607292016-02-05 15:25:58 -0500121 private static final String ALLOW_ATT_SCREEN_OFF = "visualScreenOff";
122 private static final String ALLOW_ATT_SCREEN_ON = "visualScreenOn";
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500123 private static final String DISALLOW_TAG = "disallow";
124 private static final String DISALLOW_ATT_VISUAL_EFFECTS = "visualEffects";
Beverly86d076f2018-04-17 14:44:52 -0400125 private static final String STATE_TAG = "state";
126 private static final String STATE_ATT_CHANNELS_BYPASSING_DND = "areChannelsBypassingDnd";
John Spurlock056c5192014-04-20 21:52:01 -0400127
Beverly12196702018-12-12 15:05:51 -0500128 // zen policy visual effects attributes
129 private static final String SHOW_ATT_FULL_SCREEN_INTENT = "showFullScreenIntent";
130 private static final String SHOW_ATT_LIGHTS = "showLights";
131 private static final String SHOW_ATT_PEEK = "shoePeek";
132 private static final String SHOW_ATT_STATUS_BAR_ICONS = "showStatusBarIcons";
133 private static final String SHOW_ATT_BADGES = "showBadges";
134 private static final String SHOW_ATT_AMBIENT = "showAmbient";
135 private static final String SHOW_ATT_NOTIFICATION_LIST = "showNotificationList";
136
John Spurlock3b98b3f2014-05-01 09:08:48 -0400137 private static final String CONDITION_ATT_ID = "id";
John Spurlock4db0d982014-08-13 09:19:03 -0400138 private static final String CONDITION_ATT_SUMMARY = "summary";
139 private static final String CONDITION_ATT_LINE1 = "line1";
140 private static final String CONDITION_ATT_LINE2 = "line2";
141 private static final String CONDITION_ATT_ICON = "icon";
142 private static final String CONDITION_ATT_STATE = "state";
143 private static final String CONDITION_ATT_FLAGS = "flags";
John Spurlock3b98b3f2014-05-01 09:08:48 -0400144
Beverly12196702018-12-12 15:05:51 -0500145 private static final String ZEN_POLICY_TAG = "zen_policy";
146
John Spurlockb2278d62015-04-07 12:47:12 -0400147 private static final String MANUAL_TAG = "manual";
148 private static final String AUTOMATIC_TAG = "automatic";
149
John Spurlockd60258f2015-04-30 09:30:52 -0400150 private static final String RULE_ATT_ID = "ruleId";
John Spurlockb2278d62015-04-07 12:47:12 -0400151 private static final String RULE_ATT_ENABLED = "enabled";
152 private static final String RULE_ATT_SNOOZING = "snoozing";
153 private static final String RULE_ATT_NAME = "name";
154 private static final String RULE_ATT_COMPONENT = "component";
Julia Reynolds68062072018-08-06 15:38:21 -0400155 private static final String RULE_ATT_CONFIG_ACTIVITY = "configActivity";
John Spurlockb2278d62015-04-07 12:47:12 -0400156 private static final String RULE_ATT_ZEN = "zen";
157 private static final String RULE_ATT_CONDITION_ID = "conditionId";
Julia Reynolds56106ff2015-09-30 14:42:53 -0400158 private static final String RULE_ATT_CREATION_TIME = "creationTime";
Julia Reynolds44ad6ff2016-07-06 09:47:45 -0400159 private static final String RULE_ATT_ENABLER = "enabler";
Beverlyf3b92d22018-09-18 15:13:17 -0400160 private static final String RULE_ATT_MODIFIED = "modified";
John Spurlock856edeb2014-06-01 20:36:47 -0400161
Mathew Inwoode3807372018-08-10 09:51:03 +0100162 @UnsupportedAppUsage
Beverly04216872017-09-28 10:55:32 -0400163 public boolean allowAlarms = DEFAULT_ALLOW_ALARMS;
Beverlyd6964762018-02-16 14:07:03 -0500164 public boolean allowMedia = DEFAULT_ALLOW_MEDIA;
165 public boolean allowSystem = DEFAULT_ALLOW_SYSTEM;
John Spurlocka492d1d2015-05-05 18:30:28 -0400166 public boolean allowCalls = DEFAULT_ALLOW_CALLS;
John Spurlock1d7d2242015-04-10 08:10:22 -0400167 public boolean allowRepeatCallers = DEFAULT_ALLOW_REPEAT_CALLERS;
John Spurlocka492d1d2015-05-05 18:30:28 -0400168 public boolean allowMessages = DEFAULT_ALLOW_MESSAGES;
John Spurlockfc746f82015-04-03 13:47:14 -0400169 public boolean allowReminders = DEFAULT_ALLOW_REMINDERS;
John Spurlock2dac62c2014-09-19 15:29:06 -0400170 public boolean allowEvents = DEFAULT_ALLOW_EVENTS;
Julia Reynolds1f580572018-04-27 14:48:36 -0400171 public int allowCallsFrom = DEFAULT_CALLS_SOURCE;
John Spurlocka492d1d2015-05-05 18:30:28 -0400172 public int allowMessagesFrom = DEFAULT_SOURCE;
Xiaohui Chenddbe4ca2015-08-13 16:20:56 -0700173 public int user = UserHandle.USER_SYSTEM;
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500174 public int suppressedVisualEffects = DEFAULT_SUPPRESSED_VISUAL_EFFECTS;
Beverly86d076f2018-04-17 14:44:52 -0400175 public boolean areChannelsBypassingDnd = DEFAULT_CHANNELS_BYPASSING_DND;
Beverly3bae4e52018-02-07 12:32:02 -0500176 public int version;
John Spurlock056c5192014-04-20 21:52:01 -0400177
John Spurlockb2278d62015-04-07 12:47:12 -0400178 public ZenRule manualRule;
Mathew Inwoode3807372018-08-10 09:51:03 +0100179 @UnsupportedAppUsage
John Spurlockb2278d62015-04-07 12:47:12 -0400180 public ArrayMap<String, ZenRule> automaticRules = new ArrayMap<>();
John Spurlock056c5192014-04-20 21:52:01 -0400181
Mathew Inwoode3807372018-08-10 09:51:03 +0100182 @UnsupportedAppUsage
John Spurlock056c5192014-04-20 21:52:01 -0400183 public ZenModeConfig() { }
184
185 public ZenModeConfig(Parcel source) {
186 allowCalls = source.readInt() == 1;
John Spurlock1d7d2242015-04-10 08:10:22 -0400187 allowRepeatCallers = source.readInt() == 1;
John Spurlock056c5192014-04-20 21:52:01 -0400188 allowMessages = source.readInt() == 1;
John Spurlockfc746f82015-04-03 13:47:14 -0400189 allowReminders = source.readInt() == 1;
John Spurlock2dac62c2014-09-19 15:29:06 -0400190 allowEvents = source.readInt() == 1;
John Spurlocka492d1d2015-05-05 18:30:28 -0400191 allowCallsFrom = source.readInt();
192 allowMessagesFrom = source.readInt();
John Spurlock21258a32015-05-27 18:22:55 -0400193 user = source.readInt();
John Spurlockb2278d62015-04-07 12:47:12 -0400194 manualRule = source.readParcelable(null);
195 final int len = source.readInt();
196 if (len > 0) {
197 final String[] ids = new String[len];
198 final ZenRule[] rules = new ZenRule[len];
199 source.readStringArray(ids);
200 source.readTypedArray(rules, ZenRule.CREATOR);
201 for (int i = 0; i < len; i++) {
202 automaticRules.put(ids[i], rules[i]);
203 }
204 }
Beverly04216872017-09-28 10:55:32 -0400205 allowAlarms = source.readInt() == 1;
Beverlyd6964762018-02-16 14:07:03 -0500206 allowMedia = source.readInt() == 1;
207 allowSystem = source.readInt() == 1;
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500208 suppressedVisualEffects = source.readInt();
Beverly86d076f2018-04-17 14:44:52 -0400209 areChannelsBypassingDnd = source.readInt() == 1;
John Spurlock056c5192014-04-20 21:52:01 -0400210 }
211
212 @Override
213 public void writeToParcel(Parcel dest, int flags) {
214 dest.writeInt(allowCalls ? 1 : 0);
John Spurlock1d7d2242015-04-10 08:10:22 -0400215 dest.writeInt(allowRepeatCallers ? 1 : 0);
John Spurlock056c5192014-04-20 21:52:01 -0400216 dest.writeInt(allowMessages ? 1 : 0);
John Spurlockfc746f82015-04-03 13:47:14 -0400217 dest.writeInt(allowReminders ? 1 : 0);
John Spurlock2dac62c2014-09-19 15:29:06 -0400218 dest.writeInt(allowEvents ? 1 : 0);
John Spurlocka492d1d2015-05-05 18:30:28 -0400219 dest.writeInt(allowCallsFrom);
220 dest.writeInt(allowMessagesFrom);
John Spurlock21258a32015-05-27 18:22:55 -0400221 dest.writeInt(user);
John Spurlockb2278d62015-04-07 12:47:12 -0400222 dest.writeParcelable(manualRule, 0);
223 if (!automaticRules.isEmpty()) {
224 final int len = automaticRules.size();
225 final String[] ids = new String[len];
226 final ZenRule[] rules = new ZenRule[len];
227 for (int i = 0; i < len; i++) {
228 ids[i] = automaticRules.keyAt(i);
229 rules[i] = automaticRules.valueAt(i);
230 }
231 dest.writeInt(len);
232 dest.writeStringArray(ids);
233 dest.writeTypedArray(rules, 0);
234 } else {
235 dest.writeInt(0);
236 }
Beverly04216872017-09-28 10:55:32 -0400237 dest.writeInt(allowAlarms ? 1 : 0);
Beverlyd6964762018-02-16 14:07:03 -0500238 dest.writeInt(allowMedia ? 1 : 0);
239 dest.writeInt(allowSystem ? 1 : 0);
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500240 dest.writeInt(suppressedVisualEffects);
Beverly86d076f2018-04-17 14:44:52 -0400241 dest.writeInt(areChannelsBypassingDnd ? 1 : 0);
John Spurlock056c5192014-04-20 21:52:01 -0400242 }
243
244 @Override
245 public String toString() {
246 return new StringBuilder(ZenModeConfig.class.getSimpleName()).append('[')
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500247 .append("user=").append(user)
Beverly04216872017-09-28 10:55:32 -0400248 .append(",allowAlarms=").append(allowAlarms)
Beverlyd6964762018-02-16 14:07:03 -0500249 .append(",allowMedia=").append(allowMedia)
250 .append(",allowSystem=").append(allowSystem)
Beverly04216872017-09-28 10:55:32 -0400251 .append(",allowReminders=").append(allowReminders)
252 .append(",allowEvents=").append(allowEvents)
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500253 .append(",allowCalls=").append(allowCalls)
254 .append(",allowRepeatCallers=").append(allowRepeatCallers)
255 .append(",allowMessages=").append(allowMessages)
256 .append(",allowCallsFrom=").append(sourceToString(allowCallsFrom))
257 .append(",allowMessagesFrom=").append(sourceToString(allowMessagesFrom))
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500258 .append(",suppressedVisualEffects=").append(suppressedVisualEffects)
Beverly86d076f2018-04-17 14:44:52 -0400259 .append(",areChannelsBypassingDnd=").append(areChannelsBypassingDnd)
Julia Reynolds8f056002018-07-13 15:12:29 -0400260 .append(",\nautomaticRules=").append(rulesToString())
261 .append(",\nmanualRule=").append(manualRule)
Julia Reynoldsf612869ae2015-11-05 16:48:55 -0500262 .append(']').toString();
John Spurlock056c5192014-04-20 21:52:01 -0400263 }
264
Julia Reynolds8f056002018-07-13 15:12:29 -0400265 private String rulesToString() {
266 if (automaticRules.isEmpty()) {
267 return "{}";
268 }
269
270 StringBuilder buffer = new StringBuilder(automaticRules.size() * 28);
271 buffer.append('{');
272 for (int i = 0; i < automaticRules.size(); i++) {
273 if (i > 0) {
274 buffer.append(",\n");
275 }
276 Object value = automaticRules.valueAt(i);
277 buffer.append(value);
278 }
279 buffer.append('}');
280 return buffer.toString();
281 }
282
Julia Reynolds68062072018-08-06 15:38:21 -0400283 public Diff diff(ZenModeConfig to) {
John Spurlock21258a32015-05-27 18:22:55 -0400284 final Diff d = new Diff();
285 if (to == null) {
286 return d.addLine("config", "delete");
287 }
288 if (user != to.user) {
289 d.addLine("user", user, to.user);
290 }
Beverly04216872017-09-28 10:55:32 -0400291 if (allowAlarms != to.allowAlarms) {
292 d.addLine("allowAlarms", allowAlarms, to.allowAlarms);
293 }
Beverlyd6964762018-02-16 14:07:03 -0500294 if (allowMedia != to.allowMedia) {
295 d.addLine("allowMedia", allowMedia, to.allowMedia);
296 }
297 if (allowSystem != to.allowSystem) {
298 d.addLine("allowSystem", allowSystem, to.allowSystem);
Beverly04216872017-09-28 10:55:32 -0400299 }
John Spurlock21258a32015-05-27 18:22:55 -0400300 if (allowCalls != to.allowCalls) {
301 d.addLine("allowCalls", allowCalls, to.allowCalls);
302 }
Beverly04216872017-09-28 10:55:32 -0400303 if (allowReminders != to.allowReminders) {
304 d.addLine("allowReminders", allowReminders, to.allowReminders);
305 }
306 if (allowEvents != to.allowEvents) {
307 d.addLine("allowEvents", allowEvents, to.allowEvents);
308 }
John Spurlock21258a32015-05-27 18:22:55 -0400309 if (allowRepeatCallers != to.allowRepeatCallers) {
310 d.addLine("allowRepeatCallers", allowRepeatCallers, to.allowRepeatCallers);
311 }
312 if (allowMessages != to.allowMessages) {
313 d.addLine("allowMessages", allowMessages, to.allowMessages);
314 }
315 if (allowCallsFrom != to.allowCallsFrom) {
316 d.addLine("allowCallsFrom", allowCallsFrom, to.allowCallsFrom);
317 }
318 if (allowMessagesFrom != to.allowMessagesFrom) {
319 d.addLine("allowMessagesFrom", allowMessagesFrom, to.allowMessagesFrom);
320 }
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500321 if (suppressedVisualEffects != to.suppressedVisualEffects) {
322 d.addLine("suppressedVisualEffects", suppressedVisualEffects,
323 to.suppressedVisualEffects);
324 }
John Spurlock21258a32015-05-27 18:22:55 -0400325 final ArraySet<String> allRules = new ArraySet<>();
326 addKeys(allRules, automaticRules);
327 addKeys(allRules, to.automaticRules);
328 final int N = allRules.size();
329 for (int i = 0; i < N; i++) {
330 final String rule = allRules.valueAt(i);
331 final ZenRule fromRule = automaticRules != null ? automaticRules.get(rule) : null;
332 final ZenRule toRule = to.automaticRules != null ? to.automaticRules.get(rule) : null;
333 ZenRule.appendDiff(d, "automaticRule[" + rule + "]", fromRule, toRule);
334 }
335 ZenRule.appendDiff(d, "manualRule", manualRule, to.manualRule);
Beverly86d076f2018-04-17 14:44:52 -0400336
337 if (areChannelsBypassingDnd != to.areChannelsBypassingDnd) {
338 d.addLine("areChannelsBypassingDnd", areChannelsBypassingDnd,
339 to.areChannelsBypassingDnd);
340 }
John Spurlock21258a32015-05-27 18:22:55 -0400341 return d;
342 }
343
344 public static Diff diff(ZenModeConfig from, ZenModeConfig to) {
345 if (from == null) {
346 final Diff d = new Diff();
347 if (to != null) {
348 d.addLine("config", "insert");
349 }
350 return d;
351 }
352 return from.diff(to);
353 }
354
355 private static <T> void addKeys(ArraySet<T> set, ArrayMap<T, ?> map) {
356 if (map != null) {
357 for (int i = 0; i < map.size(); i++) {
358 set.add(map.keyAt(i));
359 }
360 }
361 }
362
John Spurlockb2278d62015-04-07 12:47:12 -0400363 public boolean isValid() {
364 if (!isValidManualRule(manualRule)) return false;
365 final int N = automaticRules.size();
366 for (int i = 0; i < N; i++) {
367 if (!isValidAutomaticRule(automaticRules.valueAt(i))) return false;
368 }
369 return true;
370 }
371
372 private static boolean isValidManualRule(ZenRule rule) {
373 return rule == null || Global.isValidZenMode(rule.zenMode) && sameCondition(rule);
374 }
375
376 private static boolean isValidAutomaticRule(ZenRule rule) {
377 return rule != null && !TextUtils.isEmpty(rule.name) && Global.isValidZenMode(rule.zenMode)
378 && rule.conditionId != null && sameCondition(rule);
379 }
380
381 private static boolean sameCondition(ZenRule rule) {
382 if (rule == null) return false;
383 if (rule.conditionId == null) {
384 return rule.condition == null;
385 } else {
386 return rule.condition == null || rule.conditionId.equals(rule.condition.id);
387 }
388 }
389
John Spurlockd9c75db2015-04-28 11:19:13 -0400390 private static int[] generateMinuteBuckets() {
391 final int maxHrs = 12;
392 final int[] buckets = new int[maxHrs + 3];
393 buckets[0] = 15;
394 buckets[1] = 30;
395 buckets[2] = 45;
396 for (int i = 1; i <= maxHrs; i++) {
397 buckets[2 + i] = 60 * i;
398 }
399 return buckets;
400 }
401
Chris Wren99f963e2014-05-28 16:52:42 -0400402 public static String sourceToString(int source) {
403 switch (source) {
404 case SOURCE_ANYONE:
405 return "anyone";
406 case SOURCE_CONTACT:
407 return "contacts";
408 case SOURCE_STAR:
409 return "stars";
410 default:
411 return "UNKNOWN";
412 }
413 }
414
John Spurlock056c5192014-04-20 21:52:01 -0400415 @Override
416 public boolean equals(Object o) {
417 if (!(o instanceof ZenModeConfig)) return false;
418 if (o == this) return true;
419 final ZenModeConfig other = (ZenModeConfig) o;
Beverly04216872017-09-28 10:55:32 -0400420 return other.allowAlarms == allowAlarms
Beverlyd6964762018-02-16 14:07:03 -0500421 && other.allowMedia == allowMedia
422 && other.allowSystem == allowSystem
Beverly04216872017-09-28 10:55:32 -0400423 && other.allowCalls == allowCalls
John Spurlock1d7d2242015-04-10 08:10:22 -0400424 && other.allowRepeatCallers == allowRepeatCallers
John Spurlock056c5192014-04-20 21:52:01 -0400425 && other.allowMessages == allowMessages
John Spurlocka492d1d2015-05-05 18:30:28 -0400426 && other.allowCallsFrom == allowCallsFrom
427 && other.allowMessagesFrom == allowMessagesFrom
John Spurlockfc746f82015-04-03 13:47:14 -0400428 && other.allowReminders == allowReminders
John Spurlock2dac62c2014-09-19 15:29:06 -0400429 && other.allowEvents == allowEvents
John Spurlock21258a32015-05-27 18:22:55 -0400430 && other.user == user
John Spurlockb2278d62015-04-07 12:47:12 -0400431 && Objects.equals(other.automaticRules, automaticRules)
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500432 && Objects.equals(other.manualRule, manualRule)
Beverly86d076f2018-04-17 14:44:52 -0400433 && other.suppressedVisualEffects == suppressedVisualEffects
434 && other.areChannelsBypassingDnd == areChannelsBypassingDnd;
John Spurlock056c5192014-04-20 21:52:01 -0400435 }
436
437 @Override
438 public int hashCode() {
Beverlyd6964762018-02-16 14:07:03 -0500439 return Objects.hash(allowAlarms, allowMedia, allowSystem, allowCalls,
Beverly04216872017-09-28 10:55:32 -0400440 allowRepeatCallers, allowMessages,
441 allowCallsFrom, allowMessagesFrom, allowReminders, allowEvents,
Julia Reynolds1f580572018-04-27 14:48:36 -0400442 user, automaticRules, manualRule,
Beverly86d076f2018-04-17 14:44:52 -0400443 suppressedVisualEffects, areChannelsBypassingDnd);
John Spurlock056c5192014-04-20 21:52:01 -0400444 }
445
John Spurlockb2278d62015-04-07 12:47:12 -0400446 private static String toDayList(int[] days) {
447 if (days == null || days.length == 0) return "";
448 final StringBuilder sb = new StringBuilder();
449 for (int i = 0; i < days.length; i++) {
450 if (i > 0) sb.append('.');
451 sb.append(days[i]);
452 }
453 return sb.toString();
John Spurlockae641c92014-06-30 18:11:40 -0400454 }
455
John Spurlockb2278d62015-04-07 12:47:12 -0400456 private static int[] tryParseDayList(String dayList, String sep) {
457 if (dayList == null) return null;
458 final String[] tokens = dayList.split(sep);
John Spurlockae641c92014-06-30 18:11:40 -0400459 if (tokens.length == 0) return null;
460 final int[] rt = new int[tokens.length];
461 for (int i = 0; i < tokens.length; i++) {
462 final int day = tryParseInt(tokens[i], -1);
463 if (day == -1) return null;
464 rt[i] = day;
465 }
466 return rt;
467 }
468
469 private static int tryParseInt(String value, int defValue) {
470 if (TextUtils.isEmpty(value)) return defValue;
471 try {
Narayan Kamatha09b4d22016-04-15 18:32:45 +0100472 return Integer.parseInt(value);
John Spurlockae641c92014-06-30 18:11:40 -0400473 } catch (NumberFormatException e) {
474 return defValue;
475 }
John Spurlock056c5192014-04-20 21:52:01 -0400476 }
477
John Spurlockd60258f2015-04-30 09:30:52 -0400478 private static long tryParseLong(String value, long defValue) {
479 if (TextUtils.isEmpty(value)) return defValue;
480 try {
Tobias Thierer28532d02016-04-21 14:52:10 +0100481 return Long.parseLong(value);
John Spurlockd60258f2015-04-30 09:30:52 -0400482 } catch (NumberFormatException e) {
483 return defValue;
484 }
485 }
486
Beverlycf0fbcec2018-09-07 16:52:16 -0400487 private static Long tryParseLong(String value, Long defValue) {
488 if (TextUtils.isEmpty(value)) return defValue;
489 try {
490 return Long.parseLong(value);
491 } catch (NumberFormatException e) {
492 return defValue;
493 }
494 }
495
Julia Reynolds206c7e92016-09-15 10:38:03 -0400496 public static ZenModeConfig readXml(XmlPullParser parser)
John Spurlock056c5192014-04-20 21:52:01 -0400497 throws XmlPullParserException, IOException {
498 int type = parser.getEventType();
499 if (type != XmlPullParser.START_TAG) return null;
500 String tag = parser.getName();
501 if (!ZEN_TAG.equals(tag)) return null;
502 final ZenModeConfig rt = new ZenModeConfig();
Beverly3bae4e52018-02-07 12:32:02 -0500503 rt.version = safeInt(parser, ZEN_ATT_VERSION, XML_VERSION);
John Spurlock21258a32015-05-27 18:22:55 -0400504 rt.user = safeInt(parser, ZEN_ATT_USER, rt.user);
Julia Reynolds1f580572018-04-27 14:48:36 -0400505 boolean readSuppressedEffects = false;
John Spurlock056c5192014-04-20 21:52:01 -0400506 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
507 tag = parser.getName();
John Spurlock3b98b3f2014-05-01 09:08:48 -0400508 if (type == XmlPullParser.END_TAG && ZEN_TAG.equals(tag)) {
John Spurlock3b98b3f2014-05-01 09:08:48 -0400509 return rt;
510 }
John Spurlock056c5192014-04-20 21:52:01 -0400511 if (type == XmlPullParser.START_TAG) {
512 if (ALLOW_TAG.equals(tag)) {
Beverly04216872017-09-28 10:55:32 -0400513 rt.allowCalls = safeBoolean(parser, ALLOW_ATT_CALLS,
514 DEFAULT_ALLOW_CALLS);
John Spurlock1d7d2242015-04-10 08:10:22 -0400515 rt.allowRepeatCallers = safeBoolean(parser, ALLOW_ATT_REPEAT_CALLERS,
516 DEFAULT_ALLOW_REPEAT_CALLERS);
Beverly04216872017-09-28 10:55:32 -0400517 rt.allowMessages = safeBoolean(parser, ALLOW_ATT_MESSAGES,
518 DEFAULT_ALLOW_MESSAGES);
John Spurlockfc746f82015-04-03 13:47:14 -0400519 rt.allowReminders = safeBoolean(parser, ALLOW_ATT_REMINDERS,
520 DEFAULT_ALLOW_REMINDERS);
John Spurlock2dac62c2014-09-19 15:29:06 -0400521 rt.allowEvents = safeBoolean(parser, ALLOW_ATT_EVENTS, DEFAULT_ALLOW_EVENTS);
John Spurlocka492d1d2015-05-05 18:30:28 -0400522 final int from = safeInt(parser, ALLOW_ATT_FROM, -1);
523 final int callsFrom = safeInt(parser, ALLOW_ATT_CALLS_FROM, -1);
524 final int messagesFrom = safeInt(parser, ALLOW_ATT_MESSAGES_FROM, -1);
525 if (isValidSource(callsFrom) && isValidSource(messagesFrom)) {
526 rt.allowCallsFrom = callsFrom;
527 rt.allowMessagesFrom = messagesFrom;
528 } else if (isValidSource(from)) {
529 Slog.i(TAG, "Migrating existing shared 'from': " + sourceToString(from));
530 rt.allowCallsFrom = from;
531 rt.allowMessagesFrom = from;
532 } else {
Julia Reynolds10a7cc12018-05-03 10:56:24 -0400533 rt.allowCallsFrom = DEFAULT_CALLS_SOURCE;
John Spurlocka492d1d2015-05-05 18:30:28 -0400534 rt.allowMessagesFrom = DEFAULT_SOURCE;
Chris Wren99f963e2014-05-28 16:52:42 -0400535 }
Beverly04216872017-09-28 10:55:32 -0400536 rt.allowAlarms = safeBoolean(parser, ALLOW_ATT_ALARMS, DEFAULT_ALLOW_ALARMS);
Beverlyd6964762018-02-16 14:07:03 -0500537 rt.allowMedia = safeBoolean(parser, ALLOW_ATT_MEDIA,
538 DEFAULT_ALLOW_MEDIA);
539 rt.allowSystem = safeBoolean(parser, ALLOW_ATT_SYSTEM, DEFAULT_ALLOW_SYSTEM);
Julia Reynolds1f580572018-04-27 14:48:36 -0400540
541 // migrate old suppressed visual effects fields, if they still exist in the xml
542 Boolean allowWhenScreenOff = unsafeBoolean(parser, ALLOW_ATT_SCREEN_OFF);
543 if (allowWhenScreenOff != null) {
544 readSuppressedEffects = true;
Julia Reynolds45013872018-07-12 14:24:56 -0400545 if (!allowWhenScreenOff) {
Julia Reynolds1f580572018-04-27 14:48:36 -0400546 rt.suppressedVisualEffects |= SUPPRESSED_EFFECT_LIGHTS
547 | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
548 }
549 }
550 Boolean allowWhenScreenOn = unsafeBoolean(parser, ALLOW_ATT_SCREEN_ON);
551 if (allowWhenScreenOn != null) {
552 readSuppressedEffects = true;
Julia Reynolds45013872018-07-12 14:24:56 -0400553 if (!allowWhenScreenOn) {
Julia Reynolds1f580572018-04-27 14:48:36 -0400554 rt.suppressedVisualEffects |= SUPPRESSED_EFFECT_PEEK;
555 }
556 }
557 if (readSuppressedEffects) {
558 Slog.d(TAG, "Migrated visual effects to " + rt.suppressedVisualEffects);
559 }
560 } else if (DISALLOW_TAG.equals(tag) && !readSuppressedEffects) {
561 // only read from suppressed visual effects field if we haven't just migrated
562 // the values from allowOn/allowOff, lest we wipe out those settings
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500563 rt.suppressedVisualEffects = safeInt(parser, DISALLOW_ATT_VISUAL_EFFECTS,
564 DEFAULT_SUPPRESSED_VISUAL_EFFECTS);
John Spurlockb2278d62015-04-07 12:47:12 -0400565 } else if (MANUAL_TAG.equals(tag)) {
John Spurlock995a7492015-05-28 22:13:03 -0400566 rt.manualRule = readRuleXml(parser);
John Spurlockb2278d62015-04-07 12:47:12 -0400567 } else if (AUTOMATIC_TAG.equals(tag)) {
568 final String id = parser.getAttributeValue(null, RULE_ATT_ID);
John Spurlock995a7492015-05-28 22:13:03 -0400569 final ZenRule automaticRule = readRuleXml(parser);
John Spurlockb2278d62015-04-07 12:47:12 -0400570 if (id != null && automaticRule != null) {
Julia Reynolds56106ff2015-09-30 14:42:53 -0400571 automaticRule.id = id;
John Spurlockb2278d62015-04-07 12:47:12 -0400572 rt.automaticRules.put(id, automaticRule);
John Spurlock4db0d982014-08-13 09:19:03 -0400573 }
Beverly86d076f2018-04-17 14:44:52 -0400574 } else if (STATE_TAG.equals(tag)) {
575 rt.areChannelsBypassingDnd = safeBoolean(parser,
576 STATE_ATT_CHANNELS_BYPASSING_DND, DEFAULT_CHANNELS_BYPASSING_DND);
John Spurlock056c5192014-04-20 21:52:01 -0400577 }
578 }
579 }
John Spurlock3b98b3f2014-05-01 09:08:48 -0400580 throw new IllegalStateException("Failed to reach END_DOCUMENT");
John Spurlock056c5192014-04-20 21:52:01 -0400581 }
582
Beverly4e2f76c2018-03-16 15:43:49 -0400583 /**
584 * Writes XML of current ZenModeConfig
585 * @param out serializer
586 * @param version uses XML_VERSION if version is null
587 * @throws IOException
588 */
589 public void writeXml(XmlSerializer out, Integer version) throws IOException {
John Spurlock056c5192014-04-20 21:52:01 -0400590 out.startTag(null, ZEN_TAG);
Beverly4e2f76c2018-03-16 15:43:49 -0400591 out.attribute(null, ZEN_ATT_VERSION, version == null
592 ? Integer.toString(XML_VERSION) : Integer.toString(version));
John Spurlock21258a32015-05-27 18:22:55 -0400593 out.attribute(null, ZEN_ATT_USER, Integer.toString(user));
John Spurlock056c5192014-04-20 21:52:01 -0400594 out.startTag(null, ALLOW_TAG);
595 out.attribute(null, ALLOW_ATT_CALLS, Boolean.toString(allowCalls));
John Spurlock1d7d2242015-04-10 08:10:22 -0400596 out.attribute(null, ALLOW_ATT_REPEAT_CALLERS, Boolean.toString(allowRepeatCallers));
John Spurlock056c5192014-04-20 21:52:01 -0400597 out.attribute(null, ALLOW_ATT_MESSAGES, Boolean.toString(allowMessages));
John Spurlockfc746f82015-04-03 13:47:14 -0400598 out.attribute(null, ALLOW_ATT_REMINDERS, Boolean.toString(allowReminders));
John Spurlock2dac62c2014-09-19 15:29:06 -0400599 out.attribute(null, ALLOW_ATT_EVENTS, Boolean.toString(allowEvents));
John Spurlocka492d1d2015-05-05 18:30:28 -0400600 out.attribute(null, ALLOW_ATT_CALLS_FROM, Integer.toString(allowCallsFrom));
601 out.attribute(null, ALLOW_ATT_MESSAGES_FROM, Integer.toString(allowMessagesFrom));
Beverly04216872017-09-28 10:55:32 -0400602 out.attribute(null, ALLOW_ATT_ALARMS, Boolean.toString(allowAlarms));
Beverlyd6964762018-02-16 14:07:03 -0500603 out.attribute(null, ALLOW_ATT_MEDIA, Boolean.toString(allowMedia));
604 out.attribute(null, ALLOW_ATT_SYSTEM, Boolean.toString(allowSystem));
John Spurlock056c5192014-04-20 21:52:01 -0400605 out.endTag(null, ALLOW_TAG);
606
Julia Reynoldsccc6ae62018-03-01 16:24:49 -0500607 out.startTag(null, DISALLOW_TAG);
608 out.attribute(null, DISALLOW_ATT_VISUAL_EFFECTS, Integer.toString(suppressedVisualEffects));
609 out.endTag(null, DISALLOW_TAG);
610
John Spurlockb2278d62015-04-07 12:47:12 -0400611 if (manualRule != null) {
612 out.startTag(null, MANUAL_TAG);
613 writeRuleXml(manualRule, out);
614 out.endTag(null, MANUAL_TAG);
John Spurlock056c5192014-04-20 21:52:01 -0400615 }
John Spurlockb2278d62015-04-07 12:47:12 -0400616 final int N = automaticRules.size();
617 for (int i = 0; i < N; i++) {
618 final String id = automaticRules.keyAt(i);
619 final ZenRule automaticRule = automaticRules.valueAt(i);
620 out.startTag(null, AUTOMATIC_TAG);
621 out.attribute(null, RULE_ATT_ID, id);
622 writeRuleXml(automaticRule, out);
623 out.endTag(null, AUTOMATIC_TAG);
John Spurlock856edeb2014-06-01 20:36:47 -0400624 }
Beverly86d076f2018-04-17 14:44:52 -0400625
626 out.startTag(null, STATE_TAG);
627 out.attribute(null, STATE_ATT_CHANNELS_BYPASSING_DND,
628 Boolean.toString(areChannelsBypassingDnd));
629 out.endTag(null, STATE_TAG);
630
John Spurlock056c5192014-04-20 21:52:01 -0400631 out.endTag(null, ZEN_TAG);
632 }
633
John Spurlock995a7492015-05-28 22:13:03 -0400634 public static ZenRule readRuleXml(XmlPullParser parser) {
John Spurlockb2278d62015-04-07 12:47:12 -0400635 final ZenRule rt = new ZenRule();
636 rt.enabled = safeBoolean(parser, RULE_ATT_ENABLED, true);
John Spurlockb2278d62015-04-07 12:47:12 -0400637 rt.name = parser.getAttributeValue(null, RULE_ATT_NAME);
638 final String zen = parser.getAttributeValue(null, RULE_ATT_ZEN);
639 rt.zenMode = tryParseZenMode(zen, -1);
640 if (rt.zenMode == -1) {
641 Slog.w(TAG, "Bad zen mode in rule xml:" + zen);
642 return null;
643 }
644 rt.conditionId = safeUri(parser, RULE_ATT_CONDITION_ID);
645 rt.component = safeComponentName(parser, RULE_ATT_COMPONENT);
Julia Reynolds68062072018-08-06 15:38:21 -0400646 rt.configurationActivity = safeComponentName(parser, RULE_ATT_CONFIG_ACTIVITY);
647 rt.pkg = (rt.component != null)
648 ? rt.component.getPackageName()
649 : (rt.configurationActivity != null)
650 ? rt.configurationActivity.getPackageName()
651 : null;
Julia Reynolds56106ff2015-09-30 14:42:53 -0400652 rt.creationTime = safeLong(parser, RULE_ATT_CREATION_TIME, 0);
Julia Reynolds44ad6ff2016-07-06 09:47:45 -0400653 rt.enabler = parser.getAttributeValue(null, RULE_ATT_ENABLER);
John Spurlockb2278d62015-04-07 12:47:12 -0400654 rt.condition = readConditionXml(parser);
Beverlybf16c142017-11-27 16:21:34 -0500655
656 // all default rules and user created rules updated to zenMode important interruptions
657 if (rt.zenMode != Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
658 && Condition.isValidId(rt.conditionId, SYSTEM_AUTHORITY)) {
659 Slog.i(TAG, "Updating zenMode of automatic rule " + rt.name);
660 rt.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
661 }
Beverlyf3b92d22018-09-18 15:13:17 -0400662 rt.modified = safeBoolean(parser, RULE_ATT_MODIFIED, false);
Beverly12196702018-12-12 15:05:51 -0500663 rt.zenPolicy = readZenPolicyXml(parser);
John Spurlockd60258f2015-04-30 09:30:52 -0400664 return rt;
John Spurlockb2278d62015-04-07 12:47:12 -0400665 }
666
667 public static void writeRuleXml(ZenRule rule, XmlSerializer out) throws IOException {
668 out.attribute(null, RULE_ATT_ENABLED, Boolean.toString(rule.enabled));
John Spurlockb2278d62015-04-07 12:47:12 -0400669 if (rule.name != null) {
670 out.attribute(null, RULE_ATT_NAME, rule.name);
671 }
672 out.attribute(null, RULE_ATT_ZEN, Integer.toString(rule.zenMode));
673 if (rule.component != null) {
674 out.attribute(null, RULE_ATT_COMPONENT, rule.component.flattenToString());
675 }
Julia Reynolds68062072018-08-06 15:38:21 -0400676 if (rule.configurationActivity != null) {
677 out.attribute(null, RULE_ATT_CONFIG_ACTIVITY,
678 rule.configurationActivity.flattenToString());
679 }
John Spurlockb2278d62015-04-07 12:47:12 -0400680 if (rule.conditionId != null) {
681 out.attribute(null, RULE_ATT_CONDITION_ID, rule.conditionId.toString());
682 }
Julia Reynolds56106ff2015-09-30 14:42:53 -0400683 out.attribute(null, RULE_ATT_CREATION_TIME, Long.toString(rule.creationTime));
Julia Reynolds44ad6ff2016-07-06 09:47:45 -0400684 if (rule.enabler != null) {
685 out.attribute(null, RULE_ATT_ENABLER, rule.enabler);
686 }
John Spurlockb2278d62015-04-07 12:47:12 -0400687 if (rule.condition != null) {
688 writeConditionXml(rule.condition, out);
689 }
Beverly12196702018-12-12 15:05:51 -0500690 if (rule.zenPolicy != null) {
691 writeZenPolicyXml(rule.zenPolicy, out);
692 }
Beverlyf3b92d22018-09-18 15:13:17 -0400693 out.attribute(null, RULE_ATT_MODIFIED, Boolean.toString(rule.modified));
John Spurlockb2278d62015-04-07 12:47:12 -0400694 }
695
John Spurlock4db0d982014-08-13 09:19:03 -0400696 public static Condition readConditionXml(XmlPullParser parser) {
697 final Uri id = safeUri(parser, CONDITION_ATT_ID);
John Spurlockb2278d62015-04-07 12:47:12 -0400698 if (id == null) return null;
John Spurlock4db0d982014-08-13 09:19:03 -0400699 final String summary = parser.getAttributeValue(null, CONDITION_ATT_SUMMARY);
700 final String line1 = parser.getAttributeValue(null, CONDITION_ATT_LINE1);
701 final String line2 = parser.getAttributeValue(null, CONDITION_ATT_LINE2);
702 final int icon = safeInt(parser, CONDITION_ATT_ICON, -1);
703 final int state = safeInt(parser, CONDITION_ATT_STATE, -1);
704 final int flags = safeInt(parser, CONDITION_ATT_FLAGS, -1);
705 try {
706 return new Condition(id, summary, line1, line2, icon, state, flags);
707 } catch (IllegalArgumentException e) {
708 Slog.w(TAG, "Unable to read condition xml", e);
709 return null;
710 }
711 }
712
713 public static void writeConditionXml(Condition c, XmlSerializer out) throws IOException {
714 out.attribute(null, CONDITION_ATT_ID, c.id.toString());
715 out.attribute(null, CONDITION_ATT_SUMMARY, c.summary);
716 out.attribute(null, CONDITION_ATT_LINE1, c.line1);
717 out.attribute(null, CONDITION_ATT_LINE2, c.line2);
718 out.attribute(null, CONDITION_ATT_ICON, Integer.toString(c.icon));
719 out.attribute(null, CONDITION_ATT_STATE, Integer.toString(c.state));
720 out.attribute(null, CONDITION_ATT_FLAGS, Integer.toString(c.flags));
721 }
722
Beverly12196702018-12-12 15:05:51 -0500723 /**
724 * Read the zen policy from xml
725 * Returns null if no zen policy exists
726 */
727 public static ZenPolicy readZenPolicyXml(XmlPullParser parser) {
728 boolean policySet = false;
729
730 ZenPolicy.Builder builder = new ZenPolicy.Builder();
731 final int calls = safeInt(parser, ALLOW_ATT_CALLS_FROM, ZenPolicy.PEOPLE_TYPE_UNSET);
732 final int messages = safeInt(parser, ALLOW_ATT_MESSAGES_FROM, ZenPolicy.PEOPLE_TYPE_UNSET);
733 final int repeatCallers = safeInt(parser, ALLOW_ATT_REPEAT_CALLERS, ZenPolicy.STATE_UNSET);
734 final int alarms = safeInt(parser, ALLOW_ATT_ALARMS, ZenPolicy.STATE_UNSET);
735 final int media = safeInt(parser, ALLOW_ATT_MEDIA, ZenPolicy.STATE_UNSET);
736 final int system = safeInt(parser, ALLOW_ATT_SYSTEM, ZenPolicy.STATE_UNSET);
737 final int events = safeInt(parser, ALLOW_ATT_EVENTS, ZenPolicy.STATE_UNSET);
738 final int reminders = safeInt(parser, ALLOW_ATT_REMINDERS, ZenPolicy.STATE_UNSET);
739
740 if (calls != ZenPolicy.PEOPLE_TYPE_UNSET) {
741 builder.allowCalls(calls);
742 policySet = true;
743 }
744 if (messages != ZenPolicy.PEOPLE_TYPE_UNSET) {
745 builder.allowMessages(messages);
746 policySet = true;
747 }
748 if (repeatCallers != ZenPolicy.STATE_UNSET) {
749 builder.allowRepeatCallers(repeatCallers == ZenPolicy.STATE_ALLOW);
750 policySet = true;
751 }
752 if (alarms != ZenPolicy.STATE_UNSET) {
753 builder.allowAlarms(alarms == ZenPolicy.STATE_ALLOW);
754 policySet = true;
755 }
756 if (media != ZenPolicy.STATE_UNSET) {
757 builder.allowMedia(media == ZenPolicy.STATE_ALLOW);
758 policySet = true;
759 }
760 if (system != ZenPolicy.STATE_UNSET) {
761 builder.allowSystem(system == ZenPolicy.STATE_ALLOW);
762 policySet = true;
763 }
764 if (events != ZenPolicy.STATE_UNSET) {
765 builder.allowEvents(events == ZenPolicy.STATE_ALLOW);
766 policySet = true;
767 }
768 if (reminders != ZenPolicy.STATE_UNSET) {
769 builder.allowReminders(reminders == ZenPolicy.STATE_ALLOW);
770 policySet = true;
771 }
772
773 final int fullScreenIntent = safeInt(parser, SHOW_ATT_FULL_SCREEN_INTENT,
774 ZenPolicy.STATE_UNSET);
775 final int lights = safeInt(parser, SHOW_ATT_LIGHTS, ZenPolicy.STATE_UNSET);
776 final int peek = safeInt(parser, SHOW_ATT_PEEK, ZenPolicy.STATE_UNSET);
777 final int statusBar = safeInt(parser, SHOW_ATT_STATUS_BAR_ICONS, ZenPolicy.STATE_UNSET);
778 final int badges = safeInt(parser, SHOW_ATT_BADGES, ZenPolicy.STATE_UNSET);
779 final int ambient = safeInt(parser, SHOW_ATT_AMBIENT, ZenPolicy.STATE_UNSET);
780 final int notificationList = safeInt(parser, SHOW_ATT_NOTIFICATION_LIST,
781 ZenPolicy.STATE_UNSET);
782
783 if (fullScreenIntent != ZenPolicy.STATE_UNSET) {
784 builder.showFullScreenIntent(fullScreenIntent == ZenPolicy.STATE_ALLOW);
785 policySet = true;
786 }
787 if (lights != ZenPolicy.STATE_UNSET) {
788 builder.showLights(lights == ZenPolicy.STATE_ALLOW);
789 policySet = true;
790 }
791 if (peek != ZenPolicy.STATE_UNSET) {
792 builder.showPeeking(peek == ZenPolicy.STATE_ALLOW);
793 policySet = true;
794 }
795 if (statusBar != ZenPolicy.STATE_UNSET) {
796 builder.showStatusBarIcons(statusBar == ZenPolicy.STATE_ALLOW);
797 policySet = true;
798 }
799 if (badges != ZenPolicy.STATE_UNSET) {
800 builder.showBadges(badges == ZenPolicy.STATE_ALLOW);
801 policySet = true;
802 }
803 if (ambient != ZenPolicy.STATE_UNSET) {
804 builder.showInAmbientDisplay(ambient == ZenPolicy.STATE_ALLOW);
805 policySet = true;
806 }
807 if (notificationList != ZenPolicy.STATE_UNSET) {
808 builder.showInNotificationList(notificationList == ZenPolicy.STATE_ALLOW);
809 policySet = true;
810 }
811
812 if (policySet) {
813 return builder.build();
814 }
815 return null;
816 }
817
818 /**
819 * Writes zen policy to xml
820 */
821 public static void writeZenPolicyXml(ZenPolicy policy, XmlSerializer out)
822 throws IOException {
823 writeZenPolicyState(ALLOW_ATT_CALLS_FROM, policy.getPriorityCallSenders(), out);
824 writeZenPolicyState(ALLOW_ATT_MESSAGES_FROM, policy.getPriorityMessageSenders(), out);
825 writeZenPolicyState(ALLOW_ATT_REPEAT_CALLERS, policy.getPriorityCategoryRepeatCallers(),
826 out);
827 writeZenPolicyState(ALLOW_ATT_ALARMS, policy.getPriorityCategoryAlarms(), out);
828 writeZenPolicyState(ALLOW_ATT_MEDIA, policy.getPriorityCategoryMedia(), out);
829 writeZenPolicyState(ALLOW_ATT_SYSTEM, policy.getPriorityCategorySystem(), out);
830 writeZenPolicyState(ALLOW_ATT_REMINDERS, policy.getPriorityCategoryReminders(), out);
831 writeZenPolicyState(ALLOW_ATT_EVENTS, policy.getPriorityCategoryEvents(), out);
832
833 writeZenPolicyState(SHOW_ATT_FULL_SCREEN_INTENT, policy.getVisualEffectFullScreenIntent(),
834 out);
835 writeZenPolicyState(SHOW_ATT_LIGHTS, policy.getVisualEffectLights(), out);
836 writeZenPolicyState(SHOW_ATT_PEEK, policy.getVisualEffectPeek(), out);
837 writeZenPolicyState(SHOW_ATT_STATUS_BAR_ICONS, policy.getVisualEffectStatusBar(), out);
838 writeZenPolicyState(SHOW_ATT_BADGES, policy.getVisualEffectBadge(), out);
839 writeZenPolicyState(SHOW_ATT_AMBIENT, policy.getVisualEffectAmbient(), out);
840 writeZenPolicyState(SHOW_ATT_NOTIFICATION_LIST, policy.getVisualEffectNotificationList(),
841 out);
842 }
843
844 private static void writeZenPolicyState(String attr, int val, XmlSerializer out)
845 throws IOException {
846 if (Objects.equals(attr, ALLOW_ATT_CALLS_FROM)
847 || Objects.equals(attr, ALLOW_ATT_MESSAGES_FROM)) {
848 if (val != ZenPolicy.PEOPLE_TYPE_UNSET) {
849 out.attribute(null, attr, Integer.toString(val));
850 }
851 } else {
852 if (val != ZenPolicy.STATE_UNSET) {
853 out.attribute(null, attr, Integer.toString(val));
854 }
855 }
856 }
857
John Spurlock056c5192014-04-20 21:52:01 -0400858 public static boolean isValidHour(int val) {
859 return val >= 0 && val < 24;
860 }
861
862 public static boolean isValidMinute(int val) {
863 return val >= 0 && val < 60;
864 }
865
John Spurlocka492d1d2015-05-05 18:30:28 -0400866 private static boolean isValidSource(int source) {
867 return source >= SOURCE_ANYONE && source <= MAX_SOURCE;
868 }
869
Julia Reynolds1f580572018-04-27 14:48:36 -0400870 private static Boolean unsafeBoolean(XmlPullParser parser, String att) {
871 final String val = parser.getAttributeValue(null, att);
872 if (TextUtils.isEmpty(val)) return null;
873 return Boolean.parseBoolean(val);
874 }
875
John Spurlock056c5192014-04-20 21:52:01 -0400876 private static boolean safeBoolean(XmlPullParser parser, String att, boolean defValue) {
877 final String val = parser.getAttributeValue(null, att);
Julia Reynolds1998ee52016-02-11 13:49:08 -0500878 return safeBoolean(val, defValue);
879 }
880
881 private static boolean safeBoolean(String val, boolean defValue) {
John Spurlock056c5192014-04-20 21:52:01 -0400882 if (TextUtils.isEmpty(val)) return defValue;
Tobias Thiererb0800dc2016-04-21 17:51:41 +0100883 return Boolean.parseBoolean(val);
John Spurlock056c5192014-04-20 21:52:01 -0400884 }
885
886 private static int safeInt(XmlPullParser parser, String att, int defValue) {
887 final String val = parser.getAttributeValue(null, att);
John Spurlockae641c92014-06-30 18:11:40 -0400888 return tryParseInt(val, defValue);
John Spurlock056c5192014-04-20 21:52:01 -0400889 }
890
John Spurlock3b98b3f2014-05-01 09:08:48 -0400891 private static ComponentName safeComponentName(XmlPullParser parser, String att) {
892 final String val = parser.getAttributeValue(null, att);
893 if (TextUtils.isEmpty(val)) return null;
894 return ComponentName.unflattenFromString(val);
895 }
896
897 private static Uri safeUri(XmlPullParser parser, String att) {
898 final String val = parser.getAttributeValue(null, att);
899 if (TextUtils.isEmpty(val)) return null;
900 return Uri.parse(val);
901 }
902
Julia Reynolds56106ff2015-09-30 14:42:53 -0400903 private static long safeLong(XmlPullParser parser, String att, long defValue) {
904 final String val = parser.getAttributeValue(null, att);
905 return tryParseLong(val, defValue);
906 }
907
John Spurlock056c5192014-04-20 21:52:01 -0400908 @Override
909 public int describeContents() {
910 return 0;
911 }
912
913 public ZenModeConfig copy() {
914 final Parcel parcel = Parcel.obtain();
915 try {
916 writeToParcel(parcel, 0);
917 parcel.setDataPosition(0);
918 return new ZenModeConfig(parcel);
919 } finally {
920 parcel.recycle();
921 }
922 }
923
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700924 public static final @android.annotation.NonNull Parcelable.Creator<ZenModeConfig> CREATOR
John Spurlock056c5192014-04-20 21:52:01 -0400925 = new Parcelable.Creator<ZenModeConfig>() {
926 @Override
927 public ZenModeConfig createFromParcel(Parcel source) {
928 return new ZenModeConfig(source);
929 }
930
931 @Override
932 public ZenModeConfig[] newArray(int size) {
933 return new ZenModeConfig[size];
934 }
935 };
John Spurlock856edeb2014-06-01 20:36:47 -0400936
Beverly174d7412018-08-22 16:34:41 -0400937 /**
Beverly174d7412018-08-22 16:34:41 -0400938 * Converts a zenPolicy to a notificationPolicy using this ZenModeConfig's values as its
939 * defaults for all unset values in zenPolicy
940 */
941 public Policy toNotificationPolicy(ZenPolicy zenPolicy) {
942 NotificationManager.Policy defaultPolicy = toNotificationPolicy();
943 int priorityCategories = 0;
944 int suppressedVisualEffects = 0;
945 int callSenders = defaultPolicy.priorityCallSenders;
946 int messageSenders = defaultPolicy.priorityMessageSenders;
947
948 if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_REMINDERS,
949 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_REMINDERS, defaultPolicy))) {
Beverly12196702018-12-12 15:05:51 -0500950 priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS;
Beverly174d7412018-08-22 16:34:41 -0400951 }
952
953 if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_EVENTS,
954 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_EVENTS, defaultPolicy))) {
955 priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS;
956 }
957
958 if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_MESSAGES,
959 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_MESSAGES, defaultPolicy))) {
960 priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES;
Beverly877d5192019-02-01 14:02:58 -0500961 messageSenders = getNotificationPolicySenders(zenPolicy.getPriorityMessageSenders(),
962 messageSenders);
Beverly174d7412018-08-22 16:34:41 -0400963 }
964
965 if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_CALLS,
966 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_CALLS, defaultPolicy))) {
967 priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS;
Beverly877d5192019-02-01 14:02:58 -0500968 callSenders = getNotificationPolicySenders(zenPolicy.getPriorityCallSenders(),
969 callSenders);
Beverly174d7412018-08-22 16:34:41 -0400970 }
971
972 if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS,
973 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_REPEAT_CALLERS,
974 defaultPolicy))) {
975 priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS;
976 }
977
978 if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_ALARMS,
979 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_ALARMS, defaultPolicy))) {
980 priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS;
981 }
982
983 if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_MEDIA,
984 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_MEDIA, defaultPolicy))) {
985 priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA;
986 }
987
988 if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_SYSTEM,
989 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_SYSTEM, defaultPolicy))) {
990 priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM;
991 }
992
Beverly12196702018-12-12 15:05:51 -0500993 boolean suppressFullScreenIntent = !zenPolicy.isVisualEffectAllowed(
994 ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT,
Beverly174d7412018-08-22 16:34:41 -0400995 isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT,
Beverly12196702018-12-12 15:05:51 -0500996 defaultPolicy));
997
998 boolean suppressLights = !zenPolicy.isVisualEffectAllowed(
999 ZenPolicy.VISUAL_EFFECT_LIGHTS,
1000 isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_LIGHTS,
1001 defaultPolicy));
1002
1003 boolean suppressAmbient = !zenPolicy.isVisualEffectAllowed(
1004 ZenPolicy.VISUAL_EFFECT_AMBIENT,
1005 isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_AMBIENT,
1006 defaultPolicy));
1007
1008 if (suppressFullScreenIntent && suppressLights && suppressAmbient) {
1009 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
1010 }
1011
1012 if (suppressFullScreenIntent) {
Beverly174d7412018-08-22 16:34:41 -04001013 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
1014 }
1015
Beverly12196702018-12-12 15:05:51 -05001016 if (suppressLights) {
Beverly174d7412018-08-22 16:34:41 -04001017 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS;
1018 }
1019
1020 if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_PEEK,
1021 isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_PEEK,
1022 defaultPolicy))) {
1023 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_PEEK;
Beverly12196702018-12-12 15:05:51 -05001024 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_ON;
Beverly174d7412018-08-22 16:34:41 -04001025 }
1026
1027 if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_STATUS_BAR,
1028 isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_STATUS_BAR,
1029 defaultPolicy))) {
1030 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_STATUS_BAR;
1031 }
1032
1033 if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_BADGE,
1034 isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_BADGE,
1035 defaultPolicy))) {
1036 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE;
1037 }
1038
Beverly12196702018-12-12 15:05:51 -05001039 if (suppressAmbient) {
Beverly174d7412018-08-22 16:34:41 -04001040 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_AMBIENT;
1041 }
1042
1043 if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST,
1044 isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST,
1045 defaultPolicy))) {
1046 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
1047 }
1048
1049 return new NotificationManager.Policy(priorityCategories, callSenders,
Beverlyff2df9b2018-10-10 16:54:10 -04001050 messageSenders, suppressedVisualEffects, defaultPolicy.state);
Beverly174d7412018-08-22 16:34:41 -04001051 }
1052
1053 private boolean isPriorityCategoryEnabled(int categoryType, Policy policy) {
1054 return (policy.priorityCategories & categoryType) != 0;
1055 }
1056
1057 private boolean isVisualEffectAllowed(int visualEffect, Policy policy) {
1058 return (policy.suppressedVisualEffects & visualEffect) == 0;
1059 }
1060
Beverly877d5192019-02-01 14:02:58 -05001061 private int getNotificationPolicySenders(@ZenPolicy.PeopleType int senders,
1062 int defaultPolicySender) {
Beverly174d7412018-08-22 16:34:41 -04001063 switch (senders) {
1064 case ZenPolicy.PEOPLE_TYPE_ANYONE:
1065 return Policy.PRIORITY_SENDERS_ANY;
1066 case ZenPolicy.PEOPLE_TYPE_CONTACTS:
1067 return Policy.PRIORITY_SENDERS_CONTACTS;
1068 case ZenPolicy.PEOPLE_TYPE_STARRED:
Beverly174d7412018-08-22 16:34:41 -04001069 return Policy.PRIORITY_SENDERS_STARRED;
Beverly877d5192019-02-01 14:02:58 -05001070 default:
1071 return defaultPolicySender;
Beverly174d7412018-08-22 16:34:41 -04001072 }
Beverly174d7412018-08-22 16:34:41 -04001073 }
1074
Beverly12196702018-12-12 15:05:51 -05001075
1076 /**
1077 * Maps NotificationManager.Policy senders type to ZenPolicy.PeopleType
1078 */
1079 public static @ZenPolicy.PeopleType int getZenPolicySenders(int senders) {
1080 switch (senders) {
1081 case Policy.PRIORITY_SENDERS_ANY:
1082 return ZenPolicy.PEOPLE_TYPE_ANYONE;
1083 case Policy.PRIORITY_SENDERS_CONTACTS:
1084 return ZenPolicy.PEOPLE_TYPE_CONTACTS;
1085 case Policy.PRIORITY_SENDERS_STARRED:
1086 default:
1087 return ZenPolicy.PEOPLE_TYPE_STARRED;
1088 }
1089 }
1090
1091
John Spurlock1fc476d2015-04-14 16:05:20 -04001092 public Policy toNotificationPolicy() {
1093 int priorityCategories = 0;
John Spurlocka492d1d2015-05-05 18:30:28 -04001094 int priorityCallSenders = Policy.PRIORITY_SENDERS_CONTACTS;
1095 int priorityMessageSenders = Policy.PRIORITY_SENDERS_CONTACTS;
John Spurlock1fc476d2015-04-14 16:05:20 -04001096 if (allowCalls) {
1097 priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS;
1098 }
1099 if (allowMessages) {
1100 priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES;
1101 }
1102 if (allowEvents) {
1103 priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS;
1104 }
1105 if (allowReminders) {
1106 priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS;
1107 }
1108 if (allowRepeatCallers) {
1109 priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS;
1110 }
Beverly04216872017-09-28 10:55:32 -04001111 if (allowAlarms) {
1112 priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS;
1113 }
Beverlyd6964762018-02-16 14:07:03 -05001114 if (allowMedia) {
1115 priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA;
1116 }
1117 if (allowSystem) {
1118 priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM;
Beverly04216872017-09-28 10:55:32 -04001119 }
John Spurlocka492d1d2015-05-05 18:30:28 -04001120 priorityCallSenders = sourceToPrioritySenders(allowCallsFrom, priorityCallSenders);
1121 priorityMessageSenders = sourceToPrioritySenders(allowMessagesFrom, priorityMessageSenders);
Beverlyff2df9b2018-10-10 16:54:10 -04001122
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05001123 return new Policy(priorityCategories, priorityCallSenders, priorityMessageSenders,
Beverly86d076f2018-04-17 14:44:52 -04001124 suppressedVisualEffects, areChannelsBypassingDnd
1125 ? Policy.STATE_CHANNELS_BYPASSING_DND : 0);
John Spurlocka492d1d2015-05-05 18:30:28 -04001126 }
1127
Beverlybe6d3522017-11-20 11:01:59 -05001128 /**
1129 * Creates scheduleCalendar from a condition id
1130 * @param conditionId
1131 * @return ScheduleCalendar with info populated with conditionId
1132 */
1133 public static ScheduleCalendar toScheduleCalendar(Uri conditionId) {
1134 final ScheduleInfo schedule = ZenModeConfig.tryParseScheduleConditionId(conditionId);
1135 if (schedule == null || schedule.days == null || schedule.days.length == 0) return null;
1136 final ScheduleCalendar sc = new ScheduleCalendar();
1137 sc.setSchedule(schedule);
1138 sc.setTimeZone(TimeZone.getDefault());
1139 return sc;
1140 }
1141
John Spurlocka492d1d2015-05-05 18:30:28 -04001142 private static int sourceToPrioritySenders(int source, int def) {
1143 switch (source) {
1144 case SOURCE_ANYONE: return Policy.PRIORITY_SENDERS_ANY;
1145 case SOURCE_CONTACT: return Policy.PRIORITY_SENDERS_CONTACTS;
1146 case SOURCE_STAR: return Policy.PRIORITY_SENDERS_STARRED;
1147 default: return def;
John Spurlock1fc476d2015-04-14 16:05:20 -04001148 }
John Spurlocka492d1d2015-05-05 18:30:28 -04001149 }
1150
1151 private static int prioritySendersToSource(int prioritySenders, int def) {
1152 switch (prioritySenders) {
1153 case Policy.PRIORITY_SENDERS_CONTACTS: return SOURCE_CONTACT;
1154 case Policy.PRIORITY_SENDERS_STARRED: return SOURCE_STAR;
1155 case Policy.PRIORITY_SENDERS_ANY: return SOURCE_ANYONE;
1156 default: return def;
1157 }
John Spurlock1fc476d2015-04-14 16:05:20 -04001158 }
1159
1160 public void applyNotificationPolicy(Policy policy) {
1161 if (policy == null) return;
Beverly04216872017-09-28 10:55:32 -04001162 allowAlarms = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS) != 0;
Beverlyd6964762018-02-16 14:07:03 -05001163 allowMedia = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MEDIA) != 0;
1164 allowSystem = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_SYSTEM) != 0;
John Spurlock1fc476d2015-04-14 16:05:20 -04001165 allowEvents = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_EVENTS) != 0;
1166 allowReminders = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REMINDERS) != 0;
Beverly04216872017-09-28 10:55:32 -04001167 allowCalls = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_CALLS) != 0;
1168 allowMessages = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MESSAGES) != 0;
John Spurlock1fc476d2015-04-14 16:05:20 -04001169 allowRepeatCallers = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REPEAT_CALLERS)
1170 != 0;
John Spurlock80774932015-05-07 17:38:50 -04001171 allowCallsFrom = prioritySendersToSource(policy.priorityCallSenders, allowCallsFrom);
1172 allowMessagesFrom = prioritySendersToSource(policy.priorityMessageSenders,
1173 allowMessagesFrom);
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05001174 if (policy.suppressedVisualEffects != Policy.SUPPRESSED_EFFECTS_UNSET) {
Julia Reynoldsccc6ae62018-03-01 16:24:49 -05001175 suppressedVisualEffects = policy.suppressedVisualEffects;
Julia Reynoldsf612869ae2015-11-05 16:48:55 -05001176 }
Beverly86d076f2018-04-17 14:44:52 -04001177 if (policy.state != Policy.STATE_UNSET) {
1178 areChannelsBypassingDnd = (policy.state & Policy.STATE_CHANNELS_BYPASSING_DND) != 0;
1179 }
John Spurlock1fc476d2015-04-14 16:05:20 -04001180 }
1181
Selim Cinek9c4a7072014-11-21 17:44:34 +01001182 public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle) {
John Spurlockbbde2672015-05-13 15:42:04 -04001183 return toTimeCondition(context, minutesFromNow, userHandle, false /*shortVersion*/);
1184 }
1185
1186 public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle,
1187 boolean shortVersion) {
Jason Monk4dd81462014-09-08 10:13:59 -04001188 final long now = System.currentTimeMillis();
1189 final long millis = minutesFromNow == 0 ? ZERO_VALUE_MS : minutesFromNow * MINUTES_MS;
Julia Reynolds0842fe82015-10-01 14:34:22 -04001190 return toTimeCondition(context, now + millis, minutesFromNow, userHandle, shortVersion);
Jason Monk4dd81462014-09-08 10:13:59 -04001191 }
1192
Julia Reynolds0842fe82015-10-01 14:34:22 -04001193 public static Condition toTimeCondition(Context context, long time, int minutes,
John Spurlockbbde2672015-05-13 15:42:04 -04001194 int userHandle, boolean shortVersion) {
Julia Reynolds0842fe82015-10-01 14:34:22 -04001195 final int num;
1196 String summary, line1, line2;
Julia Reynolds7635ab02017-07-25 08:34:44 -04001197 final CharSequence formattedTime =
1198 getFormattedTime(context, time, isToday(time), userHandle);
Julia Reynolds0842fe82015-10-01 14:34:22 -04001199 final Resources res = context.getResources();
John Spurlockc90e6fe2014-10-28 11:21:42 -04001200 if (minutes < 60) {
1201 // display as minutes
1202 num = minutes;
Julia Reynolds0842fe82015-10-01 14:34:22 -04001203 int summaryResId = shortVersion ? R.plurals.zen_mode_duration_minutes_summary_short
John Spurlockbbde2672015-05-13 15:42:04 -04001204 : R.plurals.zen_mode_duration_minutes_summary;
Julia Reynolds0842fe82015-10-01 14:34:22 -04001205 summary = res.getQuantityString(summaryResId, num, num, formattedTime);
1206 int line1ResId = shortVersion ? R.plurals.zen_mode_duration_minutes_short
John Spurlockbbde2672015-05-13 15:42:04 -04001207 : R.plurals.zen_mode_duration_minutes;
Julia Reynolds0842fe82015-10-01 14:34:22 -04001208 line1 = res.getQuantityString(line1ResId, num, num, formattedTime);
1209 line2 = res.getString(R.string.zen_mode_until, formattedTime);
1210 } else if (minutes < DAY_MINUTES) {
John Spurlockc90e6fe2014-10-28 11:21:42 -04001211 // display as hours
1212 num = Math.round(minutes / 60f);
Julia Reynolds0842fe82015-10-01 14:34:22 -04001213 int summaryResId = shortVersion ? R.plurals.zen_mode_duration_hours_summary_short
John Spurlockbbde2672015-05-13 15:42:04 -04001214 : R.plurals.zen_mode_duration_hours_summary;
Julia Reynolds0842fe82015-10-01 14:34:22 -04001215 summary = res.getQuantityString(summaryResId, num, num, formattedTime);
1216 int line1ResId = shortVersion ? R.plurals.zen_mode_duration_hours_short
John Spurlockbbde2672015-05-13 15:42:04 -04001217 : R.plurals.zen_mode_duration_hours;
Julia Reynolds0842fe82015-10-01 14:34:22 -04001218 line1 = res.getQuantityString(line1ResId, num, num, formattedTime);
1219 line2 = res.getString(R.string.zen_mode_until, formattedTime);
1220 } else {
1221 // display as day/time
1222 summary = line1 = line2 = res.getString(R.string.zen_mode_until, formattedTime);
John Spurlockc90e6fe2014-10-28 11:21:42 -04001223 }
Julia Reynolds7635ab02017-07-25 08:34:44 -04001224 final Uri id = toCountdownConditionId(time, false);
John Spurlockc90e6fe2014-10-28 11:21:42 -04001225 return new Condition(id, summary, line1, line2, 0, Condition.STATE_TRUE,
Jason Monk4dd81462014-09-08 10:13:59 -04001226 Condition.FLAG_RELEVANT_NOW);
1227 }
1228
Julia Reynolds7635ab02017-07-25 08:34:44 -04001229 /**
1230 * Converts countdown to alarm parameters into a condition with user facing summary
1231 */
1232 public static Condition toNextAlarmCondition(Context context, long alarm,
Julia Reynolds0842fe82015-10-01 14:34:22 -04001233 int userHandle) {
Julia Reynolds7635ab02017-07-25 08:34:44 -04001234 boolean isSameDay = isToday(alarm);
1235 final CharSequence formattedTime = getFormattedTime(context, alarm, isSameDay, userHandle);
Julia Reynolds0842fe82015-10-01 14:34:22 -04001236 final Resources res = context.getResources();
Julia Reynolds7635ab02017-07-25 08:34:44 -04001237 final String line1 = res.getString(R.string.zen_mode_until, formattedTime);
1238 final Uri id = toCountdownConditionId(alarm, true);
Julia Reynolds0842fe82015-10-01 14:34:22 -04001239 return new Condition(id, "", line1, "", 0, Condition.STATE_TRUE,
1240 Condition.FLAG_RELEVANT_NOW);
1241 }
1242
Beverlybe6d3522017-11-20 11:01:59 -05001243 /**
1244 * Creates readable time from time in milliseconds
1245 */
1246 public static CharSequence getFormattedTime(Context context, long time, boolean isSameDay,
Julia Reynolds7635ab02017-07-25 08:34:44 -04001247 int userHandle) {
1248 String skeleton = (!isSameDay ? "EEE " : "")
1249 + (DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma");
1250 final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
1251 return DateFormat.format(pattern, time);
1252 }
1253
Beverlybe6d3522017-11-20 11:01:59 -05001254 /**
1255 * Determines whether a time in milliseconds is today or not
1256 */
1257 public static boolean isToday(long time) {
Julia Reynolds8e2d2bb2015-10-01 14:34:22 -04001258 GregorianCalendar now = new GregorianCalendar();
1259 GregorianCalendar endTime = new GregorianCalendar();
1260 endTime.setTimeInMillis(time);
1261 if (now.get(Calendar.YEAR) == endTime.get(Calendar.YEAR)
1262 && now.get(Calendar.MONTH) == endTime.get(Calendar.MONTH)
1263 && now.get(Calendar.DATE) == endTime.get(Calendar.DATE)) {
Julia Reynolds7635ab02017-07-25 08:34:44 -04001264 return true;
Julia Reynolds0842fe82015-10-01 14:34:22 -04001265 }
Julia Reynolds7635ab02017-07-25 08:34:44 -04001266 return false;
Julia Reynolds0842fe82015-10-01 14:34:22 -04001267 }
1268
John Spurlockd60258f2015-04-30 09:30:52 -04001269 // ==== Built-in system conditions ====
1270
John Spurlock37bc92c2014-11-03 11:01:51 -05001271 public static final String SYSTEM_AUTHORITY = "android";
John Spurlock4db0d982014-08-13 09:19:03 -04001272
John Spurlockd60258f2015-04-30 09:30:52 -04001273 // ==== Built-in system condition: countdown ====
1274
John Spurlock530052a2014-11-30 16:26:19 -05001275 public static final String COUNTDOWN_PATH = "countdown";
John Spurlock856edeb2014-06-01 20:36:47 -04001276
Julia Reynolds7635ab02017-07-25 08:34:44 -04001277 public static final String IS_ALARM_PATH = "alarm";
1278
1279 /**
1280 * Converts countdown condition parameters into a condition id.
1281 */
1282 public static Uri toCountdownConditionId(long time, boolean alarm) {
John Spurlock856edeb2014-06-01 20:36:47 -04001283 return new Uri.Builder().scheme(Condition.SCHEME)
John Spurlock4db0d982014-08-13 09:19:03 -04001284 .authority(SYSTEM_AUTHORITY)
John Spurlock856edeb2014-06-01 20:36:47 -04001285 .appendPath(COUNTDOWN_PATH)
1286 .appendPath(Long.toString(time))
Julia Reynolds7635ab02017-07-25 08:34:44 -04001287 .appendPath(IS_ALARM_PATH)
1288 .appendPath(Boolean.toString(alarm))
John Spurlock856edeb2014-06-01 20:36:47 -04001289 .build();
1290 }
1291
1292 public static long tryParseCountdownConditionId(Uri conditionId) {
John Spurlock4db0d982014-08-13 09:19:03 -04001293 if (!Condition.isValidId(conditionId, SYSTEM_AUTHORITY)) return 0;
Julia Reynolds7635ab02017-07-25 08:34:44 -04001294 if (conditionId.getPathSegments().size() < 2
John Spurlock856edeb2014-06-01 20:36:47 -04001295 || !COUNTDOWN_PATH.equals(conditionId.getPathSegments().get(0))) return 0;
1296 try {
1297 return Long.parseLong(conditionId.getPathSegments().get(1));
1298 } catch (RuntimeException e) {
1299 Slog.w(TAG, "Error parsing countdown condition: " + conditionId, e);
1300 return 0;
1301 }
1302 }
1303
Julia Reynolds7635ab02017-07-25 08:34:44 -04001304 /**
1305 * Returns whether this condition is a countdown condition.
1306 */
John Spurlock856edeb2014-06-01 20:36:47 -04001307 public static boolean isValidCountdownConditionId(Uri conditionId) {
1308 return tryParseCountdownConditionId(conditionId) != 0;
1309 }
John Spurlock4db0d982014-08-13 09:19:03 -04001310
Julia Reynolds7635ab02017-07-25 08:34:44 -04001311 /**
1312 * Returns whether this condition is a countdown to an alarm.
1313 */
1314 public static boolean isValidCountdownToAlarmConditionId(Uri conditionId) {
1315 if (tryParseCountdownConditionId(conditionId) != 0) {
1316 if (conditionId.getPathSegments().size() < 4
1317 || !IS_ALARM_PATH.equals(conditionId.getPathSegments().get(2))) {
1318 return false;
1319 }
1320 try {
1321 return Boolean.parseBoolean(conditionId.getPathSegments().get(3));
1322 } catch (RuntimeException e) {
1323 Slog.w(TAG, "Error parsing countdown alarm condition: " + conditionId, e);
1324 return false;
1325 }
1326 }
1327 return false;
1328 }
1329
John Spurlockd60258f2015-04-30 09:30:52 -04001330 // ==== Built-in system condition: schedule ====
1331
John Spurlockb2278d62015-04-07 12:47:12 -04001332 public static final String SCHEDULE_PATH = "schedule";
John Spurlock4db0d982014-08-13 09:19:03 -04001333
John Spurlockd60258f2015-04-30 09:30:52 -04001334 public static Uri toScheduleConditionId(ScheduleInfo schedule) {
1335 return new Uri.Builder().scheme(Condition.SCHEME)
1336 .authority(SYSTEM_AUTHORITY)
1337 .appendPath(SCHEDULE_PATH)
1338 .appendQueryParameter("days", toDayList(schedule.days))
1339 .appendQueryParameter("start", schedule.startHour + "." + schedule.startMinute)
1340 .appendQueryParameter("end", schedule.endHour + "." + schedule.endMinute)
Julia Reynolds1998ee52016-02-11 13:49:08 -05001341 .appendQueryParameter("exitAtAlarm", String.valueOf(schedule.exitAtAlarm))
John Spurlockd60258f2015-04-30 09:30:52 -04001342 .build();
1343 }
1344
1345 public static boolean isValidScheduleConditionId(Uri conditionId) {
Julia Reynolds4b318432017-11-22 10:56:18 -05001346 ScheduleInfo info;
1347 try {
1348 info = tryParseScheduleConditionId(conditionId);
1349 } catch (NullPointerException | ArrayIndexOutOfBoundsException e) {
1350 return false;
1351 }
1352
1353 if (info == null || info.days == null || info.days.length == 0) {
1354 return false;
1355 }
1356 return true;
John Spurlockd60258f2015-04-30 09:30:52 -04001357 }
1358
Beverly800ca4a2018-07-06 12:02:38 -04001359 /**
1360 * Returns whether the conditionId is a valid ScheduleCondition.
1361 * If allowNever is true, this will return true even if the ScheduleCondition never occurs.
1362 */
1363 public static boolean isValidScheduleConditionId(Uri conditionId, boolean allowNever) {
1364 ScheduleInfo info;
1365 try {
1366 info = tryParseScheduleConditionId(conditionId);
1367 } catch (NullPointerException | ArrayIndexOutOfBoundsException e) {
1368 return false;
1369 }
1370
1371 if (info == null || (!allowNever && (info.days == null || info.days.length == 0))) {
1372 return false;
1373 }
1374 return true;
1375 }
1376
Mathew Inwoode3807372018-08-10 09:51:03 +01001377 @UnsupportedAppUsage
John Spurlockd60258f2015-04-30 09:30:52 -04001378 public static ScheduleInfo tryParseScheduleConditionId(Uri conditionId) {
1379 final boolean isSchedule = conditionId != null
Julia Reynolds8f056002018-07-13 15:12:29 -04001380 && Condition.SCHEME.equals(conditionId.getScheme())
1381 && ZenModeConfig.SYSTEM_AUTHORITY.equals(conditionId.getAuthority())
John Spurlockd60258f2015-04-30 09:30:52 -04001382 && conditionId.getPathSegments().size() == 1
Julia Reynolds8f056002018-07-13 15:12:29 -04001383 && ZenModeConfig.SCHEDULE_PATH.equals(conditionId.getPathSegments().get(0));
John Spurlockd60258f2015-04-30 09:30:52 -04001384 if (!isSchedule) return null;
1385 final int[] start = tryParseHourAndMinute(conditionId.getQueryParameter("start"));
1386 final int[] end = tryParseHourAndMinute(conditionId.getQueryParameter("end"));
1387 if (start == null || end == null) return null;
1388 final ScheduleInfo rt = new ScheduleInfo();
1389 rt.days = tryParseDayList(conditionId.getQueryParameter("days"), "\\.");
1390 rt.startHour = start[0];
1391 rt.startMinute = start[1];
1392 rt.endHour = end[0];
1393 rt.endMinute = end[1];
Julia Reynolds1998ee52016-02-11 13:49:08 -05001394 rt.exitAtAlarm = safeBoolean(conditionId.getQueryParameter("exitAtAlarm"), false);
John Spurlockd60258f2015-04-30 09:30:52 -04001395 return rt;
1396 }
1397
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001398 public static ComponentName getScheduleConditionProvider() {
1399 return new ComponentName(SYSTEM_AUTHORITY, "ScheduleConditionProvider");
1400 }
1401
John Spurlockb2278d62015-04-07 12:47:12 -04001402 public static class ScheduleInfo {
Mathew Inwoode3807372018-08-10 09:51:03 +01001403 @UnsupportedAppUsage
John Spurlockb2278d62015-04-07 12:47:12 -04001404 public int[] days;
Mathew Inwoode3807372018-08-10 09:51:03 +01001405 @UnsupportedAppUsage
John Spurlockb2278d62015-04-07 12:47:12 -04001406 public int startHour;
Mathew Inwoode3807372018-08-10 09:51:03 +01001407 @UnsupportedAppUsage
John Spurlockb2278d62015-04-07 12:47:12 -04001408 public int startMinute;
Mathew Inwoode3807372018-08-10 09:51:03 +01001409 @UnsupportedAppUsage
John Spurlockb2278d62015-04-07 12:47:12 -04001410 public int endHour;
Mathew Inwoode3807372018-08-10 09:51:03 +01001411 @UnsupportedAppUsage
John Spurlockb2278d62015-04-07 12:47:12 -04001412 public int endMinute;
Julia Reynolds1998ee52016-02-11 13:49:08 -05001413 public boolean exitAtAlarm;
1414 public long nextAlarm;
John Spurlockb2278d62015-04-07 12:47:12 -04001415
1416 @Override
1417 public int hashCode() {
1418 return 0;
1419 }
1420
1421 @Override
1422 public boolean equals(Object o) {
1423 if (!(o instanceof ScheduleInfo)) return false;
1424 final ScheduleInfo other = (ScheduleInfo) o;
1425 return toDayList(days).equals(toDayList(other.days))
1426 && startHour == other.startHour
1427 && startMinute == other.startMinute
1428 && endHour == other.endHour
Julia Reynolds1998ee52016-02-11 13:49:08 -05001429 && endMinute == other.endMinute
1430 && exitAtAlarm == other.exitAtAlarm;
John Spurlockb2278d62015-04-07 12:47:12 -04001431 }
1432
1433 public ScheduleInfo copy() {
1434 final ScheduleInfo rt = new ScheduleInfo();
1435 if (days != null) {
1436 rt.days = new int[days.length];
1437 System.arraycopy(days, 0, rt.days, 0, days.length);
1438 }
1439 rt.startHour = startHour;
1440 rt.startMinute = startMinute;
1441 rt.endHour = endHour;
1442 rt.endMinute = endMinute;
Julia Reynolds1998ee52016-02-11 13:49:08 -05001443 rt.exitAtAlarm = exitAtAlarm;
1444 rt.nextAlarm = nextAlarm;
John Spurlockb2278d62015-04-07 12:47:12 -04001445 return rt;
1446 }
Julia Reynolds7f733082016-04-26 14:19:19 -04001447
1448 @Override
1449 public String toString() {
1450 return "ScheduleInfo{" +
1451 "days=" + Arrays.toString(days) +
1452 ", startHour=" + startHour +
1453 ", startMinute=" + startMinute +
1454 ", endHour=" + endHour +
1455 ", endMinute=" + endMinute +
1456 ", exitAtAlarm=" + exitAtAlarm +
Julia Reynoldsfe58f1f2016-07-19 10:18:32 -04001457 ", nextAlarm=" + ts(nextAlarm) +
Julia Reynolds7f733082016-04-26 14:19:19 -04001458 '}';
1459 }
Julia Reynoldsfe58f1f2016-07-19 10:18:32 -04001460
1461 protected static String ts(long time) {
1462 return new Date(time) + " (" + time + ")";
1463 }
John Spurlockb2278d62015-04-07 12:47:12 -04001464 }
1465
John Spurlockd60258f2015-04-30 09:30:52 -04001466 // ==== Built-in system condition: event ====
1467
1468 public static final String EVENT_PATH = "event";
1469
1470 public static Uri toEventConditionId(EventInfo event) {
John Spurlock4db0d982014-08-13 09:19:03 -04001471 return new Uri.Builder().scheme(Condition.SCHEME)
1472 .authority(SYSTEM_AUTHORITY)
John Spurlockd60258f2015-04-30 09:30:52 -04001473 .appendPath(EVENT_PATH)
John Spurlock1b8b22b2015-05-20 09:47:13 -04001474 .appendQueryParameter("userId", Long.toString(event.userId))
Beverlycf0fbcec2018-09-07 16:52:16 -04001475 .appendQueryParameter("calendar", event.calName != null ? event.calName : "")
1476 .appendQueryParameter("calendarId", event.calendarId != null
1477 ? event.calendarId.toString() : "")
John Spurlockd60258f2015-04-30 09:30:52 -04001478 .appendQueryParameter("reply", Integer.toString(event.reply))
John Spurlock4db0d982014-08-13 09:19:03 -04001479 .build();
1480 }
1481
John Spurlockd60258f2015-04-30 09:30:52 -04001482 public static boolean isValidEventConditionId(Uri conditionId) {
1483 return tryParseEventConditionId(conditionId) != null;
John Spurlockb2278d62015-04-07 12:47:12 -04001484 }
1485
John Spurlockd60258f2015-04-30 09:30:52 -04001486 public static EventInfo tryParseEventConditionId(Uri conditionId) {
1487 final boolean isEvent = conditionId != null
Julia Reynolds8f056002018-07-13 15:12:29 -04001488 && Condition.SCHEME.equals(conditionId.getScheme())
1489 && ZenModeConfig.SYSTEM_AUTHORITY.equals(conditionId.getAuthority())
John Spurlockb2278d62015-04-07 12:47:12 -04001490 && conditionId.getPathSegments().size() == 1
Julia Reynolds8f056002018-07-13 15:12:29 -04001491 && EVENT_PATH.equals(conditionId.getPathSegments().get(0));
John Spurlockd60258f2015-04-30 09:30:52 -04001492 if (!isEvent) return null;
1493 final EventInfo rt = new EventInfo();
John Spurlock1b8b22b2015-05-20 09:47:13 -04001494 rt.userId = tryParseInt(conditionId.getQueryParameter("userId"), UserHandle.USER_NULL);
Beverlycf0fbcec2018-09-07 16:52:16 -04001495 rt.calName = conditionId.getQueryParameter("calendar");
1496 if (TextUtils.isEmpty(rt.calName)) {
1497 rt.calName = null;
John Spurlock995a7492015-05-28 22:13:03 -04001498 }
Beverlycf0fbcec2018-09-07 16:52:16 -04001499 rt.calendarId = tryParseLong(conditionId.getQueryParameter("calendarId"), null);
John Spurlockd60258f2015-04-30 09:30:52 -04001500 rt.reply = tryParseInt(conditionId.getQueryParameter("reply"), 0);
John Spurlockb2278d62015-04-07 12:47:12 -04001501 return rt;
John Spurlock4db0d982014-08-13 09:19:03 -04001502 }
1503
Julia Reynoldsa47a27f2015-08-24 08:31:47 -04001504 public static ComponentName getEventConditionProvider() {
1505 return new ComponentName(SYSTEM_AUTHORITY, "EventConditionProvider");
1506 }
1507
John Spurlockd60258f2015-04-30 09:30:52 -04001508 public static class EventInfo {
John Spurlockd39af2d2015-05-05 09:49:32 -04001509 public static final int REPLY_ANY_EXCEPT_NO = 0;
1510 public static final int REPLY_YES_OR_MAYBE = 1;
John Spurlockd60258f2015-04-30 09:30:52 -04001511 public static final int REPLY_YES = 2;
1512
John Spurlock1b8b22b2015-05-20 09:47:13 -04001513 public int userId = UserHandle.USER_NULL; // USER_NULL = unspecified - use current user
Beverlycf0fbcec2018-09-07 16:52:16 -04001514 public String calName; // CalendarContract.Calendars.DISPLAY_NAME, or null for any
1515 public Long calendarId; // Calendars._ID, or null if restored from < Q calendar
John Spurlockd60258f2015-04-30 09:30:52 -04001516 public int reply;
1517
1518 @Override
1519 public int hashCode() {
Beverlycf0fbcec2018-09-07 16:52:16 -04001520 return Objects.hash(userId, calName, calendarId, reply);
John Spurlockd60258f2015-04-30 09:30:52 -04001521 }
1522
1523 @Override
1524 public boolean equals(Object o) {
1525 if (!(o instanceof EventInfo)) return false;
1526 final EventInfo other = (EventInfo) o;
John Spurlock1b8b22b2015-05-20 09:47:13 -04001527 return userId == other.userId
Beverlycf0fbcec2018-09-07 16:52:16 -04001528 && Objects.equals(calName, other.calName)
1529 && reply == other.reply
1530 && Objects.equals(calendarId, other.calendarId);
John Spurlockd60258f2015-04-30 09:30:52 -04001531 }
1532
1533 public EventInfo copy() {
1534 final EventInfo rt = new EventInfo();
John Spurlock1b8b22b2015-05-20 09:47:13 -04001535 rt.userId = userId;
Beverlycf0fbcec2018-09-07 16:52:16 -04001536 rt.calName = calName;
John Spurlockd60258f2015-04-30 09:30:52 -04001537 rt.reply = reply;
Beverlycf0fbcec2018-09-07 16:52:16 -04001538 rt.calendarId = calendarId;
John Spurlockd60258f2015-04-30 09:30:52 -04001539 return rt;
1540 }
John Spurlock1b8b22b2015-05-20 09:47:13 -04001541
1542 public static int resolveUserId(int userId) {
1543 return userId == UserHandle.USER_NULL ? ActivityManager.getCurrentUser() : userId;
John Spurlock1b8b22b2015-05-20 09:47:13 -04001544 }
John Spurlockd60258f2015-04-30 09:30:52 -04001545 }
1546
1547 // ==== End built-in system conditions ====
1548
John Spurlock4db0d982014-08-13 09:19:03 -04001549 private static int[] tryParseHourAndMinute(String value) {
1550 if (TextUtils.isEmpty(value)) return null;
1551 final int i = value.indexOf('.');
1552 if (i < 1 || i >= value.length() - 1) return null;
1553 final int hour = tryParseInt(value.substring(0, i), -1);
1554 final int minute = tryParseInt(value.substring(i + 1), -1);
1555 return isValidHour(hour) && isValidMinute(minute) ? new int[] { hour, minute } : null;
1556 }
1557
John Spurlockb2278d62015-04-07 12:47:12 -04001558 private static int tryParseZenMode(String value, int defValue) {
1559 final int rt = tryParseInt(value, defValue);
1560 return Global.isValidZenMode(rt) ? rt : defValue;
John Spurlock4db0d982014-08-13 09:19:03 -04001561 }
1562
Julia Reynolds4fe98d62015-10-06 16:23:41 -04001563 public static String newRuleId() {
John Spurlockb2278d62015-04-07 12:47:12 -04001564 return UUID.randomUUID().toString().replace("-", "");
1565 }
1566
Beverlybe6d3522017-11-20 11:01:59 -05001567 /**
1568 * Gets the name of the app associated with owner
1569 */
1570 public static String getOwnerCaption(Context context, String owner) {
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001571 final PackageManager pm = context.getPackageManager();
1572 try {
1573 final ApplicationInfo info = pm.getApplicationInfo(owner, 0);
1574 if (info != null) {
1575 final CharSequence seq = info.loadLabel(pm);
1576 if (seq != null) {
1577 final String str = seq.toString().trim();
1578 if (str.length() > 0) {
1579 return str;
1580 }
1581 }
1582 }
1583 } catch (Throwable e) {
1584 Slog.w(TAG, "Error loading owner caption", e);
1585 }
1586 return "";
1587 }
1588
John Spurlockb2278d62015-04-07 12:47:12 -04001589 public static String getConditionSummary(Context context, ZenModeConfig config,
John Spurlockbbde2672015-05-13 15:42:04 -04001590 int userHandle, boolean shortVersion) {
1591 return getConditionLine(context, config, userHandle, false /*useLine1*/, shortVersion);
John Spurlockb2278d62015-04-07 12:47:12 -04001592 }
1593
1594 private static String getConditionLine(Context context, ZenModeConfig config,
John Spurlockbbde2672015-05-13 15:42:04 -04001595 int userHandle, boolean useLine1, boolean shortVersion) {
John Spurlockb2278d62015-04-07 12:47:12 -04001596 if (config == null) return "";
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001597 String summary = "";
John Spurlockb2278d62015-04-07 12:47:12 -04001598 if (config.manualRule != null) {
1599 final Uri id = config.manualRule.conditionId;
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001600 if (config.manualRule.enabler != null) {
1601 summary = getOwnerCaption(context, config.manualRule.enabler);
1602 } else {
1603 if (id == null) {
1604 summary = context.getString(com.android.internal.R.string.zen_mode_forever);
1605 } else {
1606 final long time = tryParseCountdownConditionId(id);
1607 Condition c = config.manualRule.condition;
1608 if (time > 0) {
1609 final long now = System.currentTimeMillis();
1610 final long span = time - now;
1611 c = toTimeCondition(context, time, Math.round(span / (float) MINUTES_MS),
1612 userHandle, shortVersion);
1613 }
1614 final String rt = c == null ? "" : useLine1 ? c.line1 : c.summary;
1615 summary = TextUtils.isEmpty(rt) ? "" : rt;
1616 }
John Spurlockb2278d62015-04-07 12:47:12 -04001617 }
John Spurlockb2278d62015-04-07 12:47:12 -04001618 }
John Spurlockb2278d62015-04-07 12:47:12 -04001619 for (ZenRule automaticRule : config.automaticRules.values()) {
John Spurlock39581cc2015-04-10 11:59:01 -04001620 if (automaticRule.isAutomaticActive()) {
John Spurlockb2278d62015-04-07 12:47:12 -04001621 if (summary.isEmpty()) {
1622 summary = automaticRule.name;
1623 } else {
1624 summary = context.getResources()
1625 .getString(R.string.zen_mode_rule_name_combination, summary,
1626 automaticRule.name);
1627 }
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001628
John Spurlockb2278d62015-04-07 12:47:12 -04001629 }
1630 }
1631 return summary;
1632 }
1633
1634 public static class ZenRule implements Parcelable {
Mathew Inwoode3807372018-08-10 09:51:03 +01001635 @UnsupportedAppUsage
John Spurlockb2278d62015-04-07 12:47:12 -04001636 public boolean enabled;
Mathew Inwoode3807372018-08-10 09:51:03 +01001637 @UnsupportedAppUsage
John Spurlockb2278d62015-04-07 12:47:12 -04001638 public boolean snoozing; // user manually disabled this instance
Mathew Inwoode3807372018-08-10 09:51:03 +01001639 @UnsupportedAppUsage
Julia Reynolds361e82d32016-02-26 18:19:49 -05001640 public String name; // required for automatic
Mathew Inwoode3807372018-08-10 09:51:03 +01001641 @UnsupportedAppUsage
John Spurlockb2278d62015-04-07 12:47:12 -04001642 public int zenMode;
Mathew Inwoode3807372018-08-10 09:51:03 +01001643 @UnsupportedAppUsage
John Spurlockb2278d62015-04-07 12:47:12 -04001644 public Uri conditionId; // required for automatic
1645 public Condition condition; // optional
1646 public ComponentName component; // optional
Julia Reynolds68062072018-08-06 15:38:21 -04001647 public ComponentName configurationActivity; // optional
Julia Reynolds56106ff2015-09-30 14:42:53 -04001648 public String id; // required for automatic (unique)
Mathew Inwoode3807372018-08-10 09:51:03 +01001649 @UnsupportedAppUsage
Julia Reynolds56106ff2015-09-30 14:42:53 -04001650 public long creationTime; // required for automatic
Julia Reynolds68062072018-08-06 15:38:21 -04001651 // package name, only used for manual rules when they have turned DND on.
1652 public String enabler;
Beverly174d7412018-08-22 16:34:41 -04001653 public ZenPolicy zenPolicy;
Beverlyf3b92d22018-09-18 15:13:17 -04001654 public boolean modified; // rule has been modified from initial creation
Julia Reynolds68062072018-08-06 15:38:21 -04001655 public String pkg;
John Spurlockb2278d62015-04-07 12:47:12 -04001656
1657 public ZenRule() { }
1658
1659 public ZenRule(Parcel source) {
1660 enabled = source.readInt() == 1;
1661 snoozing = source.readInt() == 1;
1662 if (source.readInt() == 1) {
1663 name = source.readString();
1664 }
1665 zenMode = source.readInt();
1666 conditionId = source.readParcelable(null);
1667 condition = source.readParcelable(null);
1668 component = source.readParcelable(null);
Julia Reynolds68062072018-08-06 15:38:21 -04001669 configurationActivity = source.readParcelable(null);
Julia Reynolds56106ff2015-09-30 14:42:53 -04001670 if (source.readInt() == 1) {
1671 id = source.readString();
1672 }
1673 creationTime = source.readLong();
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001674 if (source.readInt() == 1) {
1675 enabler = source.readString();
1676 }
Beverly174d7412018-08-22 16:34:41 -04001677 zenPolicy = source.readParcelable(null);
Beverlyf3b92d22018-09-18 15:13:17 -04001678 modified = source.readInt() == 1;
Julia Reynolds68062072018-08-06 15:38:21 -04001679 pkg = source.readString();
John Spurlockb2278d62015-04-07 12:47:12 -04001680 }
John Spurlock4db0d982014-08-13 09:19:03 -04001681
1682 @Override
John Spurlockb2278d62015-04-07 12:47:12 -04001683 public int describeContents() {
John Spurlock4db0d982014-08-13 09:19:03 -04001684 return 0;
1685 }
1686
1687 @Override
John Spurlockb2278d62015-04-07 12:47:12 -04001688 public void writeToParcel(Parcel dest, int flags) {
1689 dest.writeInt(enabled ? 1 : 0);
1690 dest.writeInt(snoozing ? 1 : 0);
1691 if (name != null) {
1692 dest.writeInt(1);
1693 dest.writeString(name);
1694 } else {
1695 dest.writeInt(0);
1696 }
1697 dest.writeInt(zenMode);
1698 dest.writeParcelable(conditionId, 0);
1699 dest.writeParcelable(condition, 0);
1700 dest.writeParcelable(component, 0);
Julia Reynolds68062072018-08-06 15:38:21 -04001701 dest.writeParcelable(configurationActivity, 0);
Julia Reynolds56106ff2015-09-30 14:42:53 -04001702 if (id != null) {
1703 dest.writeInt(1);
1704 dest.writeString(id);
1705 } else {
1706 dest.writeInt(0);
1707 }
1708 dest.writeLong(creationTime);
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001709 if (enabler != null) {
1710 dest.writeInt(1);
1711 dest.writeString(enabler);
1712 } else {
1713 dest.writeInt(0);
1714 }
Beverly174d7412018-08-22 16:34:41 -04001715 dest.writeParcelable(zenPolicy, 0);
Beverlyf3b92d22018-09-18 15:13:17 -04001716 dest.writeInt(modified ? 1 : 0);
Julia Reynolds68062072018-08-06 15:38:21 -04001717 dest.writeString(pkg);
John Spurlockb2278d62015-04-07 12:47:12 -04001718 }
1719
1720 @Override
1721 public String toString() {
1722 return new StringBuilder(ZenRule.class.getSimpleName()).append('[')
Julia Reynolds8f056002018-07-13 15:12:29 -04001723 .append("id=").append(id)
1724 .append(",enabled=").append(String.valueOf(enabled).toUpperCase())
John Spurlockb2278d62015-04-07 12:47:12 -04001725 .append(",snoozing=").append(snoozing)
1726 .append(",name=").append(name)
1727 .append(",zenMode=").append(Global.zenModeToString(zenMode))
1728 .append(",conditionId=").append(conditionId)
1729 .append(",condition=").append(condition)
Julia Reynolds68062072018-08-06 15:38:21 -04001730 .append(",pkg=").append(pkg)
John Spurlockb2278d62015-04-07 12:47:12 -04001731 .append(",component=").append(component)
Julia Reynolds68062072018-08-06 15:38:21 -04001732 .append(",configActivity=").append(configurationActivity)
Julia Reynolds56106ff2015-09-30 14:42:53 -04001733 .append(",creationTime=").append(creationTime)
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001734 .append(",enabler=").append(enabler)
Beverly174d7412018-08-22 16:34:41 -04001735 .append(",zenPolicy=").append(zenPolicy)
Beverlyf3b92d22018-09-18 15:13:17 -04001736 .append(",modified=").append(modified)
John Spurlockb2278d62015-04-07 12:47:12 -04001737 .append(']').toString();
1738 }
1739
Kweku Adams99546332018-01-24 17:03:50 -08001740 /** @hide */
Julia Reynolds68062072018-08-06 15:38:21 -04001741 // TODO: add configuration activity
Kweku Adams99546332018-01-24 17:03:50 -08001742 public void writeToProto(ProtoOutputStream proto, long fieldId) {
1743 final long token = proto.start(fieldId);
1744
1745 proto.write(ZenRuleProto.ID, id);
1746 proto.write(ZenRuleProto.NAME, name);
1747 proto.write(ZenRuleProto.CREATION_TIME_MS, creationTime);
1748 proto.write(ZenRuleProto.ENABLED, enabled);
1749 proto.write(ZenRuleProto.ENABLER, enabler);
1750 proto.write(ZenRuleProto.IS_SNOOZING, snoozing);
1751 proto.write(ZenRuleProto.ZEN_MODE, zenMode);
1752 if (conditionId != null) {
1753 proto.write(ZenRuleProto.CONDITION_ID, conditionId.toString());
1754 }
1755 if (condition != null) {
1756 condition.writeToProto(proto, ZenRuleProto.CONDITION);
1757 }
1758 if (component != null) {
1759 component.writeToProto(proto, ZenRuleProto.COMPONENT);
1760 }
Beverlyac3159f2018-09-13 14:52:17 -04001761 if (zenPolicy != null) {
1762 zenPolicy.writeToProto(proto, ZenRuleProto.ZEN_POLICY);
1763 }
Beverlyf3b92d22018-09-18 15:13:17 -04001764 proto.write(ZenRuleProto.MODIFIED, modified);
Kweku Adams99546332018-01-24 17:03:50 -08001765 proto.end(token);
1766 }
1767
John Spurlock21258a32015-05-27 18:22:55 -04001768 private static void appendDiff(Diff d, String item, ZenRule from, ZenRule to) {
1769 if (d == null) return;
1770 if (from == null) {
1771 if (to != null) {
1772 d.addLine(item, "insert");
1773 }
1774 return;
1775 }
1776 from.appendDiff(d, item, to);
1777 }
1778
1779 private void appendDiff(Diff d, String item, ZenRule to) {
1780 if (to == null) {
1781 d.addLine(item, "delete");
1782 return;
1783 }
1784 if (enabled != to.enabled) {
1785 d.addLine(item, "enabled", enabled, to.enabled);
1786 }
1787 if (snoozing != to.snoozing) {
1788 d.addLine(item, "snoozing", snoozing, to.snoozing);
1789 }
1790 if (!Objects.equals(name, to.name)) {
1791 d.addLine(item, "name", name, to.name);
1792 }
1793 if (zenMode != to.zenMode) {
1794 d.addLine(item, "zenMode", zenMode, to.zenMode);
1795 }
1796 if (!Objects.equals(conditionId, to.conditionId)) {
1797 d.addLine(item, "conditionId", conditionId, to.conditionId);
1798 }
1799 if (!Objects.equals(condition, to.condition)) {
1800 d.addLine(item, "condition", condition, to.condition);
1801 }
1802 if (!Objects.equals(component, to.component)) {
1803 d.addLine(item, "component", component, to.component);
1804 }
Julia Reynolds68062072018-08-06 15:38:21 -04001805 if (!Objects.equals(configurationActivity, to.configurationActivity)) {
1806 d.addLine(item, "configActivity", configurationActivity, to.configurationActivity);
1807 }
Julia Reynolds56106ff2015-09-30 14:42:53 -04001808 if (!Objects.equals(id, to.id)) {
1809 d.addLine(item, "id", id, to.id);
1810 }
Julia Reynoldsc8e54e82015-11-30 16:43:05 -05001811 if (creationTime != to.creationTime) {
Julia Reynolds56106ff2015-09-30 14:42:53 -04001812 d.addLine(item, "creationTime", creationTime, to.creationTime);
1813 }
Beverly174d7412018-08-22 16:34:41 -04001814 if (!Objects.equals(enabler, to.enabler)) {
Julia Reynolds44ad6ff2016-07-06 09:47:45 -04001815 d.addLine(item, "enabler", enabler, to.enabler);
1816 }
Beverly174d7412018-08-22 16:34:41 -04001817 if (!Objects.equals(zenPolicy, to.zenPolicy)) {
1818 d.addLine(item, "zenPolicy", zenPolicy, to.zenPolicy);
1819 }
Beverlyf3b92d22018-09-18 15:13:17 -04001820 if (modified != to.modified) {
1821 d.addLine(item, "modified", modified, to.modified);
1822 }
Julia Reynolds68062072018-08-06 15:38:21 -04001823 if (pkg != to.pkg) {
1824 d.addLine(item, "pkg", pkg, to.pkg);
1825 }
John Spurlock21258a32015-05-27 18:22:55 -04001826 }
1827
John Spurlockb2278d62015-04-07 12:47:12 -04001828 @Override
John Spurlock4db0d982014-08-13 09:19:03 -04001829 public boolean equals(Object o) {
John Spurlockb2278d62015-04-07 12:47:12 -04001830 if (!(o instanceof ZenRule)) return false;
1831 if (o == this) return true;
1832 final ZenRule other = (ZenRule) o;
1833 return other.enabled == enabled
1834 && other.snoozing == snoozing
1835 && Objects.equals(other.name, name)
1836 && other.zenMode == zenMode
1837 && Objects.equals(other.conditionId, conditionId)
1838 && Objects.equals(other.condition, condition)
Julia Reynolds56106ff2015-09-30 14:42:53 -04001839 && Objects.equals(other.component, component)
Julia Reynolds68062072018-08-06 15:38:21 -04001840 && Objects.equals(other.configurationActivity, configurationActivity)
Julia Reynolds56106ff2015-09-30 14:42:53 -04001841 && Objects.equals(other.id, id)
Beverly174d7412018-08-22 16:34:41 -04001842 && Objects.equals(other.enabler, enabler)
Beverlyf3b92d22018-09-18 15:13:17 -04001843 && Objects.equals(other.zenPolicy, zenPolicy)
Julia Reynolds68062072018-08-06 15:38:21 -04001844 && Objects.equals(other.pkg, pkg)
Beverlyf3b92d22018-09-18 15:13:17 -04001845 && other.modified == modified;
John Spurlockb2278d62015-04-07 12:47:12 -04001846 }
1847
1848 @Override
1849 public int hashCode() {
1850 return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
Julia Reynolds68062072018-08-06 15:38:21 -04001851 component, configurationActivity, pkg, id, enabler, zenPolicy, modified);
John Spurlockb2278d62015-04-07 12:47:12 -04001852 }
1853
John Spurlock39581cc2015-04-10 11:59:01 -04001854 public boolean isAutomaticActive() {
Julia Reynolds68062072018-08-06 15:38:21 -04001855 return enabled && !snoozing && pkg != null && isTrueOrUnknown();
John Spurlock39581cc2015-04-10 11:59:01 -04001856 }
1857
John Spurlockb2278d62015-04-07 12:47:12 -04001858 public boolean isTrueOrUnknown() {
John Spurlock39581cc2015-04-10 11:59:01 -04001859 return condition != null && (condition.state == Condition.STATE_TRUE
1860 || condition.state == Condition.STATE_UNKNOWN);
John Spurlockb2278d62015-04-07 12:47:12 -04001861 }
1862
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -07001863 public static final @android.annotation.NonNull Parcelable.Creator<ZenRule> CREATOR
John Spurlockb2278d62015-04-07 12:47:12 -04001864 = new Parcelable.Creator<ZenRule>() {
1865 @Override
1866 public ZenRule createFromParcel(Parcel source) {
1867 return new ZenRule(source);
1868 }
1869 @Override
1870 public ZenRule[] newArray(int size) {
1871 return new ZenRule[size];
1872 }
1873 };
1874 }
1875
John Spurlock21258a32015-05-27 18:22:55 -04001876 public static class Diff {
1877 private final ArrayList<String> lines = new ArrayList<>();
1878
1879 @Override
1880 public String toString() {
1881 final StringBuilder sb = new StringBuilder("Diff[");
1882 final int N = lines.size();
1883 for (int i = 0; i < N; i++) {
1884 if (i > 0) {
Julia Reynolds8f056002018-07-13 15:12:29 -04001885 sb.append(",\n");
John Spurlock21258a32015-05-27 18:22:55 -04001886 }
1887 sb.append(lines.get(i));
1888 }
1889 return sb.append(']').toString();
1890 }
1891
1892 private Diff addLine(String item, String action) {
1893 lines.add(item + ":" + action);
1894 return this;
1895 }
1896
1897 public Diff addLine(String item, String subitem, Object from, Object to) {
1898 return addLine(item + "." + subitem, from, to);
1899 }
1900
1901 public Diff addLine(String item, Object from, Object to) {
1902 return addLine(item, from + "->" + to);
1903 }
1904 }
1905
Beverly925cde82018-01-23 09:31:23 -05001906 /**
Beverlyd6964762018-02-16 14:07:03 -05001907 * Determines whether dnd behavior should mute all notification/ringer sounds
1908 * (sounds associated with ringer volume discluding system)
Beverly925cde82018-01-23 09:31:23 -05001909 */
1910 public static boolean areAllPriorityOnlyNotificationZenSoundsMuted(NotificationManager.Policy
1911 policy) {
1912 boolean allowReminders = (policy.priorityCategories
1913 & NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS) != 0;
1914 boolean allowCalls = (policy.priorityCategories
1915 & NotificationManager.Policy.PRIORITY_CATEGORY_CALLS) != 0;
1916 boolean allowMessages = (policy.priorityCategories
1917 & NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) != 0;
1918 boolean allowEvents = (policy.priorityCategories
1919 & NotificationManager.Policy.PRIORITY_CATEGORY_EVENTS) != 0;
1920 boolean allowRepeatCallers = (policy.priorityCategories
1921 & NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS) != 0;
Beverly86d076f2018-04-17 14:44:52 -04001922 boolean areChannelsBypassingDnd = (policy.state & Policy.STATE_CHANNELS_BYPASSING_DND) != 0;
Beverly925cde82018-01-23 09:31:23 -05001923 return !allowReminders && !allowCalls && !allowMessages && !allowEvents
Beverly86d076f2018-04-17 14:44:52 -04001924 && !allowRepeatCallers && !areChannelsBypassingDnd;
Beverly925cde82018-01-23 09:31:23 -05001925 }
1926
1927 /**
Beverlyff2df9b2018-10-10 16:54:10 -04001928 * Determines whether dnd behavior should mute all sounds controlled by ringer
1929 */
1930 public static boolean areAllZenBehaviorSoundsMuted(NotificationManager.Policy
1931 policy) {
1932 boolean allowAlarms = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS) != 0;
1933 boolean allowMedia = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MEDIA) != 0;
1934 boolean allowSystem = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_SYSTEM) != 0;
1935 return !allowAlarms && !allowMedia && !allowSystem
1936 && areAllPriorityOnlyNotificationZenSoundsMuted(policy);
1937 }
1938
1939 /**
Beverlyb9826dd2018-04-12 15:02:45 -04001940 * Determines if DND is currently overriding the ringer
1941 */
Beverlyff2df9b2018-10-10 16:54:10 -04001942 public static boolean isZenOverridingRinger(int zen, Policy consolidatedPolicy) {
Beverlyb9826dd2018-04-12 15:02:45 -04001943 return zen == Global.ZEN_MODE_NO_INTERRUPTIONS
1944 || zen == Global.ZEN_MODE_ALARMS
1945 || (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
Beverlyff2df9b2018-10-10 16:54:10 -04001946 && ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(consolidatedPolicy));
Beverlyb9826dd2018-04-12 15:02:45 -04001947 }
1948
1949 /**
Beverlyd6964762018-02-16 14:07:03 -05001950 * Determines whether dnd behavior should mute all sounds controlled by ringer
Beverly925cde82018-01-23 09:31:23 -05001951 */
1952 public static boolean areAllPriorityOnlyNotificationZenSoundsMuted(ZenModeConfig config) {
1953 return !config.allowReminders && !config.allowCalls && !config.allowMessages
Beverly86d076f2018-04-17 14:44:52 -04001954 && !config.allowEvents && !config.allowRepeatCallers
1955 && !config.areChannelsBypassingDnd;
Beverly925cde82018-01-23 09:31:23 -05001956 }
1957
1958 /**
1959 * Determines whether all dnd mutes all sounds
1960 */
1961 public static boolean areAllZenBehaviorSoundsMuted(ZenModeConfig config) {
Beverlyd6964762018-02-16 14:07:03 -05001962 return !config.allowAlarms && !config.allowMedia && !config.allowSystem
Beverly925cde82018-01-23 09:31:23 -05001963 && areAllPriorityOnlyNotificationZenSoundsMuted(config);
1964 }
Beverlya5effb42018-02-01 17:08:35 -05001965
1966 /**
1967 * Returns a description of the current do not disturb settings from config.
1968 * - If turned on manually and end time is known, returns end time.
Beverlyebcaeeb2018-04-06 14:15:23 -04001969 * - If turned on manually and end time is on forever until turned off, return null if
1970 * describeForeverCondition is false, else return String describing indefinite behavior
Beverlya5effb42018-02-01 17:08:35 -05001971 * - If turned on by an automatic rule, returns the automatic rule name.
1972 * - If on due to an app, returns the app name.
1973 * - If there's a combination of rules/apps that trigger, then shows the one that will
1974 * last the longest if applicable.
Beverlyebcaeeb2018-04-06 14:15:23 -04001975 * @return null if DND is off or describeForeverCondition is false and
1976 * DND is on forever (until turned off)
Beverlya5effb42018-02-01 17:08:35 -05001977 */
Beverlyebcaeeb2018-04-06 14:15:23 -04001978 public static String getDescription(Context context, boolean zenOn, ZenModeConfig config,
1979 boolean describeForeverCondition) {
1980 if (!zenOn || config == null) {
Beverlya5effb42018-02-01 17:08:35 -05001981 return null;
1982 }
1983
1984 String secondaryText = "";
1985 long latestEndTime = -1;
1986
1987 // DND turned on by manual rule
1988 if (config.manualRule != null) {
1989 final Uri id = config.manualRule.conditionId;
1990 if (config.manualRule.enabler != null) {
1991 // app triggered manual rule
1992 String appName = getOwnerCaption(context, config.manualRule.enabler);
1993 if (!appName.isEmpty()) {
1994 secondaryText = appName;
1995 }
1996 } else {
1997 if (id == null) {
1998 // Do not disturb manually triggered to remain on forever until turned off
Beverlyebcaeeb2018-04-06 14:15:23 -04001999 if (describeForeverCondition) {
2000 return context.getString(R.string.zen_mode_forever);
2001 } else {
2002 return null;
2003 }
Beverlya5effb42018-02-01 17:08:35 -05002004 } else {
2005 latestEndTime = tryParseCountdownConditionId(id);
2006 if (latestEndTime > 0) {
2007 final CharSequence formattedTime = getFormattedTime(context,
2008 latestEndTime, isToday(latestEndTime),
2009 context.getUserId());
2010 secondaryText = context.getString(R.string.zen_mode_until, formattedTime);
2011 }
2012 }
2013 }
2014 }
2015
2016 // DND turned on by an automatic rule
2017 for (ZenRule automaticRule : config.automaticRules.values()) {
2018 if (automaticRule.isAutomaticActive()) {
2019 if (isValidEventConditionId(automaticRule.conditionId)
2020 || isValidScheduleConditionId(automaticRule.conditionId)) {
2021 // set text if automatic rule end time is the latest active rule end time
2022 long endTime = parseAutomaticRuleEndTime(context, automaticRule.conditionId);
2023 if (endTime > latestEndTime) {
2024 latestEndTime = endTime;
2025 secondaryText = automaticRule.name;
2026 }
2027 } else {
2028 // set text if 3rd party rule
2029 return automaticRule.name;
2030 }
2031 }
2032 }
2033
2034 return !secondaryText.equals("") ? secondaryText : null;
2035 }
2036
2037 private static long parseAutomaticRuleEndTime(Context context, Uri id) {
2038 if (isValidEventConditionId(id)) {
2039 // cannot look up end times for events
2040 return Long.MAX_VALUE;
2041 }
2042
2043 if (isValidScheduleConditionId(id)) {
2044 ScheduleCalendar schedule = toScheduleCalendar(id);
2045 long endTimeMs = schedule.getNextChangeTime(System.currentTimeMillis());
2046
2047 // check if automatic rule will end on next alarm
2048 if (schedule.exitAtAlarm()) {
2049 long nextAlarm = getNextAlarm(context);
2050 schedule.maybeSetNextAlarm(System.currentTimeMillis(), nextAlarm);
2051 if (schedule.shouldExitForAlarm(endTimeMs)) {
2052 return nextAlarm;
2053 }
2054 }
2055
2056 return endTimeMs;
2057 }
2058
2059 return -1;
2060 }
2061
2062 private static long getNextAlarm(Context context) {
2063 final AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
2064 final AlarmManager.AlarmClockInfo info = alarms.getNextAlarmClock(context.getUserId());
2065 return info != null ? info.getTriggerTime() : 0;
2066 }
John Spurlock056c5192014-04-20 21:52:01 -04002067}