1 /** 2 * Copyright (c) 2014, The Android Open Source Project 3 * 4 * Licensed under the Apache License, 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.service.notification; 18 19 import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE; 20 import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT; 21 import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_NONE; 22 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; 23 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS; 24 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; 25 26 import android.app.ActivityManager; 27 import android.app.AlarmManager; 28 import android.app.NotificationManager; 29 import android.app.NotificationManager.Policy; 30 import android.compat.annotation.UnsupportedAppUsage; 31 import android.content.ComponentName; 32 import android.content.Context; 33 import android.content.pm.ApplicationInfo; 34 import android.content.pm.PackageManager; 35 import android.content.res.Resources; 36 import android.net.Uri; 37 import android.os.Parcel; 38 import android.os.Parcelable; 39 import android.os.UserHandle; 40 import android.provider.Settings.Global; 41 import android.text.TextUtils; 42 import android.text.format.DateFormat; 43 import android.util.ArrayMap; 44 import android.util.ArraySet; 45 import android.util.Slog; 46 import android.util.proto.ProtoOutputStream; 47 48 import com.android.internal.R; 49 50 import org.xmlpull.v1.XmlPullParser; 51 import org.xmlpull.v1.XmlPullParserException; 52 import org.xmlpull.v1.XmlSerializer; 53 54 import java.io.IOException; 55 import java.util.ArrayList; 56 import java.util.Arrays; 57 import java.util.Calendar; 58 import java.util.Date; 59 import java.util.GregorianCalendar; 60 import java.util.List; 61 import java.util.Locale; 62 import java.util.Objects; 63 import java.util.TimeZone; 64 import java.util.UUID; 65 66 /** 67 * Persisted configuration for zen mode. 68 * 69 * @hide 70 */ 71 public class ZenModeConfig implements Parcelable { 72 private static String TAG = "ZenModeConfig"; 73 74 public static final int SOURCE_ANYONE = Policy.PRIORITY_SENDERS_ANY; 75 public static final int SOURCE_CONTACT = Policy.PRIORITY_SENDERS_CONTACTS; 76 public static final int SOURCE_STAR = Policy.PRIORITY_SENDERS_STARRED; 77 public static final int MAX_SOURCE = SOURCE_STAR; 78 private static final int DEFAULT_SOURCE = SOURCE_CONTACT; 79 private static final int DEFAULT_CALLS_SOURCE = SOURCE_STAR; 80 81 public static final String MANUAL_RULE_ID = "MANUAL_RULE"; 82 public static final String EVENTS_DEFAULT_RULE_ID = "EVENTS_DEFAULT_RULE"; 83 public static final String EVERY_NIGHT_DEFAULT_RULE_ID = "EVERY_NIGHT_DEFAULT_RULE"; 84 public static final List<String> DEFAULT_RULE_IDS = Arrays.asList(EVERY_NIGHT_DEFAULT_RULE_ID, 85 EVENTS_DEFAULT_RULE_ID); 86 87 public static final int[] ALL_DAYS = { Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY, 88 Calendar.WEDNESDAY, Calendar.THURSDAY, Calendar.FRIDAY, Calendar.SATURDAY }; 89 90 public static final int[] MINUTE_BUCKETS = generateMinuteBuckets(); 91 private static final int SECONDS_MS = 1000; 92 private static final int MINUTES_MS = 60 * SECONDS_MS; 93 private static final int DAY_MINUTES = 24 * 60; 94 private static final int ZERO_VALUE_MS = 10 * SECONDS_MS; 95 96 // Default allow categories set in readXml() from default_zen_mode_config.xml, 97 // fallback/upgrade values: 98 private static final boolean DEFAULT_ALLOW_ALARMS = true; 99 private static final boolean DEFAULT_ALLOW_MEDIA = true; 100 private static final boolean DEFAULT_ALLOW_SYSTEM = false; 101 private static final boolean DEFAULT_ALLOW_CALLS = true; 102 private static final boolean DEFAULT_ALLOW_MESSAGES = false; 103 private static final boolean DEFAULT_ALLOW_REMINDERS = false; 104 private static final boolean DEFAULT_ALLOW_EVENTS = false; 105 private static final boolean DEFAULT_ALLOW_REPEAT_CALLERS = true; 106 private static final boolean DEFAULT_ALLOW_CONV = false; 107 private static final int DEFAULT_ALLOW_CONV_FROM = ZenPolicy.CONVERSATION_SENDERS_NONE; 108 private static final boolean DEFAULT_CHANNELS_BYPASSING_DND = false; 109 private static final int DEFAULT_SUPPRESSED_VISUAL_EFFECTS = 0; 110 111 public static final int XML_VERSION = 8; 112 public static final String ZEN_TAG = "zen"; 113 private static final String ZEN_ATT_VERSION = "version"; 114 private static final String ZEN_ATT_USER = "user"; 115 private static final String ALLOW_TAG = "allow"; 116 private static final String ALLOW_ATT_ALARMS = "alarms"; 117 private static final String ALLOW_ATT_MEDIA = "media"; 118 private static final String ALLOW_ATT_SYSTEM = "system"; 119 private static final String ALLOW_ATT_CALLS = "calls"; 120 private static final String ALLOW_ATT_REPEAT_CALLERS = "repeatCallers"; 121 private static final String ALLOW_ATT_MESSAGES = "messages"; 122 private static final String ALLOW_ATT_FROM = "from"; 123 private static final String ALLOW_ATT_CALLS_FROM = "callsFrom"; 124 private static final String ALLOW_ATT_MESSAGES_FROM = "messagesFrom"; 125 private static final String ALLOW_ATT_REMINDERS = "reminders"; 126 private static final String ALLOW_ATT_EVENTS = "events"; 127 private static final String ALLOW_ATT_SCREEN_OFF = "visualScreenOff"; 128 private static final String ALLOW_ATT_SCREEN_ON = "visualScreenOn"; 129 private static final String ALLOW_ATT_CONV = "convos"; 130 private static final String ALLOW_ATT_CONV_FROM = "convosFrom"; 131 private static final String DISALLOW_TAG = "disallow"; 132 private static final String DISALLOW_ATT_VISUAL_EFFECTS = "visualEffects"; 133 private static final String STATE_TAG = "state"; 134 private static final String STATE_ATT_CHANNELS_BYPASSING_DND = "areChannelsBypassingDnd"; 135 136 // zen policy visual effects attributes 137 private static final String SHOW_ATT_FULL_SCREEN_INTENT = "showFullScreenIntent"; 138 private static final String SHOW_ATT_LIGHTS = "showLights"; 139 private static final String SHOW_ATT_PEEK = "shoePeek"; 140 private static final String SHOW_ATT_STATUS_BAR_ICONS = "showStatusBarIcons"; 141 private static final String SHOW_ATT_BADGES = "showBadges"; 142 private static final String SHOW_ATT_AMBIENT = "showAmbient"; 143 private static final String SHOW_ATT_NOTIFICATION_LIST = "showNotificationList"; 144 145 private static final String CONDITION_ATT_ID = "id"; 146 private static final String CONDITION_ATT_SUMMARY = "summary"; 147 private static final String CONDITION_ATT_LINE1 = "line1"; 148 private static final String CONDITION_ATT_LINE2 = "line2"; 149 private static final String CONDITION_ATT_ICON = "icon"; 150 private static final String CONDITION_ATT_STATE = "state"; 151 private static final String CONDITION_ATT_FLAGS = "flags"; 152 153 private static final String ZEN_POLICY_TAG = "zen_policy"; 154 155 private static final String MANUAL_TAG = "manual"; 156 private static final String AUTOMATIC_TAG = "automatic"; 157 158 private static final String RULE_ATT_ID = "ruleId"; 159 private static final String RULE_ATT_ENABLED = "enabled"; 160 private static final String RULE_ATT_SNOOZING = "snoozing"; 161 private static final String RULE_ATT_NAME = "name"; 162 private static final String RULE_ATT_COMPONENT = "component"; 163 private static final String RULE_ATT_CONFIG_ACTIVITY = "configActivity"; 164 private static final String RULE_ATT_ZEN = "zen"; 165 private static final String RULE_ATT_CONDITION_ID = "conditionId"; 166 private static final String RULE_ATT_CREATION_TIME = "creationTime"; 167 private static final String RULE_ATT_ENABLER = "enabler"; 168 private static final String RULE_ATT_MODIFIED = "modified"; 169 170 @UnsupportedAppUsage 171 public boolean allowAlarms = DEFAULT_ALLOW_ALARMS; 172 public boolean allowMedia = DEFAULT_ALLOW_MEDIA; 173 public boolean allowSystem = DEFAULT_ALLOW_SYSTEM; 174 public boolean allowCalls = DEFAULT_ALLOW_CALLS; 175 public boolean allowRepeatCallers = DEFAULT_ALLOW_REPEAT_CALLERS; 176 public boolean allowMessages = DEFAULT_ALLOW_MESSAGES; 177 public boolean allowReminders = DEFAULT_ALLOW_REMINDERS; 178 public boolean allowEvents = DEFAULT_ALLOW_EVENTS; 179 public int allowCallsFrom = DEFAULT_CALLS_SOURCE; 180 public int allowMessagesFrom = DEFAULT_SOURCE; 181 public boolean allowConversations = DEFAULT_ALLOW_CONV; 182 public int allowConversationsFrom = DEFAULT_ALLOW_CONV_FROM; 183 public int user = UserHandle.USER_SYSTEM; 184 public int suppressedVisualEffects = DEFAULT_SUPPRESSED_VISUAL_EFFECTS; 185 public boolean areChannelsBypassingDnd = DEFAULT_CHANNELS_BYPASSING_DND; 186 public int version; 187 188 public ZenRule manualRule; 189 @UnsupportedAppUsage 190 public ArrayMap<String, ZenRule> automaticRules = new ArrayMap<>(); 191 192 @UnsupportedAppUsage ZenModeConfig()193 public ZenModeConfig() { } 194 ZenModeConfig(Parcel source)195 public ZenModeConfig(Parcel source) { 196 allowCalls = source.readInt() == 1; 197 allowRepeatCallers = source.readInt() == 1; 198 allowMessages = source.readInt() == 1; 199 allowReminders = source.readInt() == 1; 200 allowEvents = source.readInt() == 1; 201 allowCallsFrom = source.readInt(); 202 allowMessagesFrom = source.readInt(); 203 user = source.readInt(); 204 manualRule = source.readParcelable(null); 205 final int len = source.readInt(); 206 if (len > 0) { 207 final String[] ids = new String[len]; 208 final ZenRule[] rules = new ZenRule[len]; 209 source.readStringArray(ids); 210 source.readTypedArray(rules, ZenRule.CREATOR); 211 for (int i = 0; i < len; i++) { 212 automaticRules.put(ids[i], rules[i]); 213 } 214 } 215 allowAlarms = source.readInt() == 1; 216 allowMedia = source.readInt() == 1; 217 allowSystem = source.readInt() == 1; 218 suppressedVisualEffects = source.readInt(); 219 areChannelsBypassingDnd = source.readInt() == 1; 220 allowConversations = source.readBoolean(); 221 allowConversationsFrom = source.readInt(); 222 } 223 224 @Override writeToParcel(Parcel dest, int flags)225 public void writeToParcel(Parcel dest, int flags) { 226 dest.writeInt(allowCalls ? 1 : 0); 227 dest.writeInt(allowRepeatCallers ? 1 : 0); 228 dest.writeInt(allowMessages ? 1 : 0); 229 dest.writeInt(allowReminders ? 1 : 0); 230 dest.writeInt(allowEvents ? 1 : 0); 231 dest.writeInt(allowCallsFrom); 232 dest.writeInt(allowMessagesFrom); 233 dest.writeInt(user); 234 dest.writeParcelable(manualRule, 0); 235 if (!automaticRules.isEmpty()) { 236 final int len = automaticRules.size(); 237 final String[] ids = new String[len]; 238 final ZenRule[] rules = new ZenRule[len]; 239 for (int i = 0; i < len; i++) { 240 ids[i] = automaticRules.keyAt(i); 241 rules[i] = automaticRules.valueAt(i); 242 } 243 dest.writeInt(len); 244 dest.writeStringArray(ids); 245 dest.writeTypedArray(rules, 0); 246 } else { 247 dest.writeInt(0); 248 } 249 dest.writeInt(allowAlarms ? 1 : 0); 250 dest.writeInt(allowMedia ? 1 : 0); 251 dest.writeInt(allowSystem ? 1 : 0); 252 dest.writeInt(suppressedVisualEffects); 253 dest.writeInt(areChannelsBypassingDnd ? 1 : 0); 254 dest.writeBoolean(allowConversations); 255 dest.writeInt(allowConversationsFrom); 256 } 257 258 @Override toString()259 public String toString() { 260 return new StringBuilder(ZenModeConfig.class.getSimpleName()).append('[') 261 .append("user=").append(user) 262 .append(",allowAlarms=").append(allowAlarms) 263 .append(",allowMedia=").append(allowMedia) 264 .append(",allowSystem=").append(allowSystem) 265 .append(",allowReminders=").append(allowReminders) 266 .append(",allowEvents=").append(allowEvents) 267 .append(",allowCalls=").append(allowCalls) 268 .append(",allowRepeatCallers=").append(allowRepeatCallers) 269 .append(",allowMessages=").append(allowMessages) 270 .append(",allowConversations=").append(allowConversations) 271 .append(",allowCallsFrom=").append(sourceToString(allowCallsFrom)) 272 .append(",allowMessagesFrom=").append(sourceToString(allowMessagesFrom)) 273 .append(",allowConvFrom=").append(ZenPolicy.conversationTypeToString 274 (allowConversationsFrom)) 275 .append(",suppressedVisualEffects=").append(suppressedVisualEffects) 276 .append(",areChannelsBypassingDnd=").append(areChannelsBypassingDnd) 277 .append(",\nautomaticRules=").append(rulesToString()) 278 .append(",\nmanualRule=").append(manualRule) 279 .append(']').toString(); 280 } 281 rulesToString()282 private String rulesToString() { 283 if (automaticRules.isEmpty()) { 284 return "{}"; 285 } 286 287 StringBuilder buffer = new StringBuilder(automaticRules.size() * 28); 288 buffer.append('{'); 289 for (int i = 0; i < automaticRules.size(); i++) { 290 if (i > 0) { 291 buffer.append(",\n"); 292 } 293 Object value = automaticRules.valueAt(i); 294 buffer.append(value); 295 } 296 buffer.append('}'); 297 return buffer.toString(); 298 } 299 diff(ZenModeConfig to)300 public Diff diff(ZenModeConfig to) { 301 final Diff d = new Diff(); 302 if (to == null) { 303 return d.addLine("config", "delete"); 304 } 305 if (user != to.user) { 306 d.addLine("user", user, to.user); 307 } 308 if (allowAlarms != to.allowAlarms) { 309 d.addLine("allowAlarms", allowAlarms, to.allowAlarms); 310 } 311 if (allowMedia != to.allowMedia) { 312 d.addLine("allowMedia", allowMedia, to.allowMedia); 313 } 314 if (allowSystem != to.allowSystem) { 315 d.addLine("allowSystem", allowSystem, to.allowSystem); 316 } 317 if (allowCalls != to.allowCalls) { 318 d.addLine("allowCalls", allowCalls, to.allowCalls); 319 } 320 if (allowReminders != to.allowReminders) { 321 d.addLine("allowReminders", allowReminders, to.allowReminders); 322 } 323 if (allowEvents != to.allowEvents) { 324 d.addLine("allowEvents", allowEvents, to.allowEvents); 325 } 326 if (allowRepeatCallers != to.allowRepeatCallers) { 327 d.addLine("allowRepeatCallers", allowRepeatCallers, to.allowRepeatCallers); 328 } 329 if (allowMessages != to.allowMessages) { 330 d.addLine("allowMessages", allowMessages, to.allowMessages); 331 } 332 if (allowCallsFrom != to.allowCallsFrom) { 333 d.addLine("allowCallsFrom", allowCallsFrom, to.allowCallsFrom); 334 } 335 if (allowMessagesFrom != to.allowMessagesFrom) { 336 d.addLine("allowMessagesFrom", allowMessagesFrom, to.allowMessagesFrom); 337 } 338 if (suppressedVisualEffects != to.suppressedVisualEffects) { 339 d.addLine("suppressedVisualEffects", suppressedVisualEffects, 340 to.suppressedVisualEffects); 341 } 342 final ArraySet<String> allRules = new ArraySet<>(); 343 addKeys(allRules, automaticRules); 344 addKeys(allRules, to.automaticRules); 345 final int N = allRules.size(); 346 for (int i = 0; i < N; i++) { 347 final String rule = allRules.valueAt(i); 348 final ZenRule fromRule = automaticRules != null ? automaticRules.get(rule) : null; 349 final ZenRule toRule = to.automaticRules != null ? to.automaticRules.get(rule) : null; 350 ZenRule.appendDiff(d, "automaticRule[" + rule + "]", fromRule, toRule); 351 } 352 ZenRule.appendDiff(d, "manualRule", manualRule, to.manualRule); 353 354 if (areChannelsBypassingDnd != to.areChannelsBypassingDnd) { 355 d.addLine("areChannelsBypassingDnd", areChannelsBypassingDnd, 356 to.areChannelsBypassingDnd); 357 } 358 return d; 359 } 360 diff(ZenModeConfig from, ZenModeConfig to)361 public static Diff diff(ZenModeConfig from, ZenModeConfig to) { 362 if (from == null) { 363 final Diff d = new Diff(); 364 if (to != null) { 365 d.addLine("config", "insert"); 366 } 367 return d; 368 } 369 return from.diff(to); 370 } 371 addKeys(ArraySet<T> set, ArrayMap<T, ?> map)372 private static <T> void addKeys(ArraySet<T> set, ArrayMap<T, ?> map) { 373 if (map != null) { 374 for (int i = 0; i < map.size(); i++) { 375 set.add(map.keyAt(i)); 376 } 377 } 378 } 379 isValid()380 public boolean isValid() { 381 if (!isValidManualRule(manualRule)) return false; 382 final int N = automaticRules.size(); 383 for (int i = 0; i < N; i++) { 384 if (!isValidAutomaticRule(automaticRules.valueAt(i))) return false; 385 } 386 return true; 387 } 388 isValidManualRule(ZenRule rule)389 private static boolean isValidManualRule(ZenRule rule) { 390 return rule == null || Global.isValidZenMode(rule.zenMode) && sameCondition(rule); 391 } 392 isValidAutomaticRule(ZenRule rule)393 private static boolean isValidAutomaticRule(ZenRule rule) { 394 return rule != null && !TextUtils.isEmpty(rule.name) && Global.isValidZenMode(rule.zenMode) 395 && rule.conditionId != null && sameCondition(rule); 396 } 397 sameCondition(ZenRule rule)398 private static boolean sameCondition(ZenRule rule) { 399 if (rule == null) return false; 400 if (rule.conditionId == null) { 401 return rule.condition == null; 402 } else { 403 return rule.condition == null || rule.conditionId.equals(rule.condition.id); 404 } 405 } 406 generateMinuteBuckets()407 private static int[] generateMinuteBuckets() { 408 final int maxHrs = 12; 409 final int[] buckets = new int[maxHrs + 3]; 410 buckets[0] = 15; 411 buckets[1] = 30; 412 buckets[2] = 45; 413 for (int i = 1; i <= maxHrs; i++) { 414 buckets[2 + i] = 60 * i; 415 } 416 return buckets; 417 } 418 sourceToString(int source)419 public static String sourceToString(int source) { 420 switch (source) { 421 case SOURCE_ANYONE: 422 return "anyone"; 423 case SOURCE_CONTACT: 424 return "contacts"; 425 case SOURCE_STAR: 426 return "stars"; 427 default: 428 return "UNKNOWN"; 429 } 430 } 431 432 @Override equals(Object o)433 public boolean equals(Object o) { 434 if (!(o instanceof ZenModeConfig)) return false; 435 if (o == this) return true; 436 final ZenModeConfig other = (ZenModeConfig) o; 437 return other.allowAlarms == allowAlarms 438 && other.allowMedia == allowMedia 439 && other.allowSystem == allowSystem 440 && other.allowCalls == allowCalls 441 && other.allowRepeatCallers == allowRepeatCallers 442 && other.allowMessages == allowMessages 443 && other.allowCallsFrom == allowCallsFrom 444 && other.allowMessagesFrom == allowMessagesFrom 445 && other.allowReminders == allowReminders 446 && other.allowEvents == allowEvents 447 && other.user == user 448 && Objects.equals(other.automaticRules, automaticRules) 449 && Objects.equals(other.manualRule, manualRule) 450 && other.suppressedVisualEffects == suppressedVisualEffects 451 && other.areChannelsBypassingDnd == areChannelsBypassingDnd 452 && other.allowConversations == allowConversations 453 && other.allowConversationsFrom == allowConversationsFrom; 454 } 455 456 @Override hashCode()457 public int hashCode() { 458 return Objects.hash(allowAlarms, allowMedia, allowSystem, allowCalls, 459 allowRepeatCallers, allowMessages, 460 allowCallsFrom, allowMessagesFrom, allowReminders, allowEvents, 461 user, automaticRules, manualRule, 462 suppressedVisualEffects, areChannelsBypassingDnd, allowConversations, 463 allowConversationsFrom); 464 } 465 toDayList(int[] days)466 private static String toDayList(int[] days) { 467 if (days == null || days.length == 0) return ""; 468 final StringBuilder sb = new StringBuilder(); 469 for (int i = 0; i < days.length; i++) { 470 if (i > 0) sb.append('.'); 471 sb.append(days[i]); 472 } 473 return sb.toString(); 474 } 475 tryParseDayList(String dayList, String sep)476 private static int[] tryParseDayList(String dayList, String sep) { 477 if (dayList == null) return null; 478 final String[] tokens = dayList.split(sep); 479 if (tokens.length == 0) return null; 480 final int[] rt = new int[tokens.length]; 481 for (int i = 0; i < tokens.length; i++) { 482 final int day = tryParseInt(tokens[i], -1); 483 if (day == -1) return null; 484 rt[i] = day; 485 } 486 return rt; 487 } 488 tryParseInt(String value, int defValue)489 private static int tryParseInt(String value, int defValue) { 490 if (TextUtils.isEmpty(value)) return defValue; 491 try { 492 return Integer.parseInt(value); 493 } catch (NumberFormatException e) { 494 return defValue; 495 } 496 } 497 tryParseLong(String value, long defValue)498 private static long tryParseLong(String value, long defValue) { 499 if (TextUtils.isEmpty(value)) return defValue; 500 try { 501 return Long.parseLong(value); 502 } catch (NumberFormatException e) { 503 return defValue; 504 } 505 } 506 tryParseLong(String value, Long defValue)507 private static Long tryParseLong(String value, Long defValue) { 508 if (TextUtils.isEmpty(value)) return defValue; 509 try { 510 return Long.parseLong(value); 511 } catch (NumberFormatException e) { 512 return defValue; 513 } 514 } 515 readXml(XmlPullParser parser)516 public static ZenModeConfig readXml(XmlPullParser parser) 517 throws XmlPullParserException, IOException { 518 int type = parser.getEventType(); 519 if (type != XmlPullParser.START_TAG) return null; 520 String tag = parser.getName(); 521 if (!ZEN_TAG.equals(tag)) return null; 522 final ZenModeConfig rt = new ZenModeConfig(); 523 rt.version = safeInt(parser, ZEN_ATT_VERSION, XML_VERSION); 524 rt.user = safeInt(parser, ZEN_ATT_USER, rt.user); 525 boolean readSuppressedEffects = false; 526 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { 527 tag = parser.getName(); 528 if (type == XmlPullParser.END_TAG && ZEN_TAG.equals(tag)) { 529 return rt; 530 } 531 if (type == XmlPullParser.START_TAG) { 532 if (ALLOW_TAG.equals(tag)) { 533 rt.allowCalls = safeBoolean(parser, ALLOW_ATT_CALLS, 534 DEFAULT_ALLOW_CALLS); 535 rt.allowRepeatCallers = safeBoolean(parser, ALLOW_ATT_REPEAT_CALLERS, 536 DEFAULT_ALLOW_REPEAT_CALLERS); 537 rt.allowMessages = safeBoolean(parser, ALLOW_ATT_MESSAGES, 538 DEFAULT_ALLOW_MESSAGES); 539 rt.allowReminders = safeBoolean(parser, ALLOW_ATT_REMINDERS, 540 DEFAULT_ALLOW_REMINDERS); 541 rt.allowConversations = safeBoolean(parser, ALLOW_ATT_CONV, DEFAULT_ALLOW_CONV); 542 rt.allowEvents = safeBoolean(parser, ALLOW_ATT_EVENTS, DEFAULT_ALLOW_EVENTS); 543 final int from = safeInt(parser, ALLOW_ATT_FROM, -1); 544 final int callsFrom = safeInt(parser, ALLOW_ATT_CALLS_FROM, -1); 545 final int messagesFrom = safeInt(parser, ALLOW_ATT_MESSAGES_FROM, -1); 546 rt.allowConversationsFrom = safeInt(parser, ALLOW_ATT_CONV_FROM, 547 DEFAULT_ALLOW_CONV_FROM); 548 if (isValidSource(callsFrom) && isValidSource(messagesFrom)) { 549 rt.allowCallsFrom = callsFrom; 550 rt.allowMessagesFrom = messagesFrom; 551 } else if (isValidSource(from)) { 552 Slog.i(TAG, "Migrating existing shared 'from': " + sourceToString(from)); 553 rt.allowCallsFrom = from; 554 rt.allowMessagesFrom = from; 555 } else { 556 rt.allowCallsFrom = DEFAULT_CALLS_SOURCE; 557 rt.allowMessagesFrom = DEFAULT_SOURCE; 558 } 559 rt.allowAlarms = safeBoolean(parser, ALLOW_ATT_ALARMS, DEFAULT_ALLOW_ALARMS); 560 rt.allowMedia = safeBoolean(parser, ALLOW_ATT_MEDIA, 561 DEFAULT_ALLOW_MEDIA); 562 rt.allowSystem = safeBoolean(parser, ALLOW_ATT_SYSTEM, DEFAULT_ALLOW_SYSTEM); 563 564 // migrate old suppressed visual effects fields, if they still exist in the xml 565 Boolean allowWhenScreenOff = unsafeBoolean(parser, ALLOW_ATT_SCREEN_OFF); 566 if (allowWhenScreenOff != null) { 567 readSuppressedEffects = true; 568 if (!allowWhenScreenOff) { 569 rt.suppressedVisualEffects |= SUPPRESSED_EFFECT_LIGHTS 570 | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; 571 } 572 } 573 Boolean allowWhenScreenOn = unsafeBoolean(parser, ALLOW_ATT_SCREEN_ON); 574 if (allowWhenScreenOn != null) { 575 readSuppressedEffects = true; 576 if (!allowWhenScreenOn) { 577 rt.suppressedVisualEffects |= SUPPRESSED_EFFECT_PEEK; 578 } 579 } 580 if (readSuppressedEffects) { 581 Slog.d(TAG, "Migrated visual effects to " + rt.suppressedVisualEffects); 582 } 583 } else if (DISALLOW_TAG.equals(tag) && !readSuppressedEffects) { 584 // only read from suppressed visual effects field if we haven't just migrated 585 // the values from allowOn/allowOff, lest we wipe out those settings 586 rt.suppressedVisualEffects = safeInt(parser, DISALLOW_ATT_VISUAL_EFFECTS, 587 DEFAULT_SUPPRESSED_VISUAL_EFFECTS); 588 } else if (MANUAL_TAG.equals(tag)) { 589 rt.manualRule = readRuleXml(parser); 590 } else if (AUTOMATIC_TAG.equals(tag)) { 591 final String id = parser.getAttributeValue(null, RULE_ATT_ID); 592 final ZenRule automaticRule = readRuleXml(parser); 593 if (id != null && automaticRule != null) { 594 automaticRule.id = id; 595 rt.automaticRules.put(id, automaticRule); 596 } 597 } else if (STATE_TAG.equals(tag)) { 598 rt.areChannelsBypassingDnd = safeBoolean(parser, 599 STATE_ATT_CHANNELS_BYPASSING_DND, DEFAULT_CHANNELS_BYPASSING_DND); 600 } 601 } 602 } 603 throw new IllegalStateException("Failed to reach END_DOCUMENT"); 604 } 605 606 /** 607 * Writes XML of current ZenModeConfig 608 * @param out serializer 609 * @param version uses XML_VERSION if version is null 610 * @throws IOException 611 */ writeXml(XmlSerializer out, Integer version)612 public void writeXml(XmlSerializer out, Integer version) throws IOException { 613 out.startTag(null, ZEN_TAG); 614 out.attribute(null, ZEN_ATT_VERSION, version == null 615 ? Integer.toString(XML_VERSION) : Integer.toString(version)); 616 out.attribute(null, ZEN_ATT_USER, Integer.toString(user)); 617 out.startTag(null, ALLOW_TAG); 618 out.attribute(null, ALLOW_ATT_CALLS, Boolean.toString(allowCalls)); 619 out.attribute(null, ALLOW_ATT_REPEAT_CALLERS, Boolean.toString(allowRepeatCallers)); 620 out.attribute(null, ALLOW_ATT_MESSAGES, Boolean.toString(allowMessages)); 621 out.attribute(null, ALLOW_ATT_REMINDERS, Boolean.toString(allowReminders)); 622 out.attribute(null, ALLOW_ATT_EVENTS, Boolean.toString(allowEvents)); 623 out.attribute(null, ALLOW_ATT_CALLS_FROM, Integer.toString(allowCallsFrom)); 624 out.attribute(null, ALLOW_ATT_MESSAGES_FROM, Integer.toString(allowMessagesFrom)); 625 out.attribute(null, ALLOW_ATT_ALARMS, Boolean.toString(allowAlarms)); 626 out.attribute(null, ALLOW_ATT_MEDIA, Boolean.toString(allowMedia)); 627 out.attribute(null, ALLOW_ATT_SYSTEM, Boolean.toString(allowSystem)); 628 out.attribute(null, ALLOW_ATT_CONV, Boolean.toString(allowConversations)); 629 out.attribute(null, ALLOW_ATT_CONV_FROM, Integer.toString(allowConversationsFrom)); 630 out.endTag(null, ALLOW_TAG); 631 632 out.startTag(null, DISALLOW_TAG); 633 out.attribute(null, DISALLOW_ATT_VISUAL_EFFECTS, Integer.toString(suppressedVisualEffects)); 634 out.endTag(null, DISALLOW_TAG); 635 636 if (manualRule != null) { 637 out.startTag(null, MANUAL_TAG); 638 writeRuleXml(manualRule, out); 639 out.endTag(null, MANUAL_TAG); 640 } 641 final int N = automaticRules.size(); 642 for (int i = 0; i < N; i++) { 643 final String id = automaticRules.keyAt(i); 644 final ZenRule automaticRule = automaticRules.valueAt(i); 645 out.startTag(null, AUTOMATIC_TAG); 646 out.attribute(null, RULE_ATT_ID, id); 647 writeRuleXml(automaticRule, out); 648 out.endTag(null, AUTOMATIC_TAG); 649 } 650 651 out.startTag(null, STATE_TAG); 652 out.attribute(null, STATE_ATT_CHANNELS_BYPASSING_DND, 653 Boolean.toString(areChannelsBypassingDnd)); 654 out.endTag(null, STATE_TAG); 655 656 out.endTag(null, ZEN_TAG); 657 } 658 readRuleXml(XmlPullParser parser)659 public static ZenRule readRuleXml(XmlPullParser parser) { 660 final ZenRule rt = new ZenRule(); 661 rt.enabled = safeBoolean(parser, RULE_ATT_ENABLED, true); 662 rt.name = parser.getAttributeValue(null, RULE_ATT_NAME); 663 final String zen = parser.getAttributeValue(null, RULE_ATT_ZEN); 664 rt.zenMode = tryParseZenMode(zen, -1); 665 if (rt.zenMode == -1) { 666 Slog.w(TAG, "Bad zen mode in rule xml:" + zen); 667 return null; 668 } 669 rt.conditionId = safeUri(parser, RULE_ATT_CONDITION_ID); 670 rt.component = safeComponentName(parser, RULE_ATT_COMPONENT); 671 rt.configurationActivity = safeComponentName(parser, RULE_ATT_CONFIG_ACTIVITY); 672 rt.pkg = (rt.component != null) 673 ? rt.component.getPackageName() 674 : (rt.configurationActivity != null) 675 ? rt.configurationActivity.getPackageName() 676 : null; 677 rt.creationTime = safeLong(parser, RULE_ATT_CREATION_TIME, 0); 678 rt.enabler = parser.getAttributeValue(null, RULE_ATT_ENABLER); 679 rt.condition = readConditionXml(parser); 680 681 // all default rules and user created rules updated to zenMode important interruptions 682 if (rt.zenMode != Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS 683 && Condition.isValidId(rt.conditionId, SYSTEM_AUTHORITY)) { 684 Slog.i(TAG, "Updating zenMode of automatic rule " + rt.name); 685 rt.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; 686 } 687 rt.modified = safeBoolean(parser, RULE_ATT_MODIFIED, false); 688 rt.zenPolicy = readZenPolicyXml(parser); 689 return rt; 690 } 691 writeRuleXml(ZenRule rule, XmlSerializer out)692 public static void writeRuleXml(ZenRule rule, XmlSerializer out) throws IOException { 693 out.attribute(null, RULE_ATT_ENABLED, Boolean.toString(rule.enabled)); 694 if (rule.name != null) { 695 out.attribute(null, RULE_ATT_NAME, rule.name); 696 } 697 out.attribute(null, RULE_ATT_ZEN, Integer.toString(rule.zenMode)); 698 if (rule.component != null) { 699 out.attribute(null, RULE_ATT_COMPONENT, rule.component.flattenToString()); 700 } 701 if (rule.configurationActivity != null) { 702 out.attribute(null, RULE_ATT_CONFIG_ACTIVITY, 703 rule.configurationActivity.flattenToString()); 704 } 705 if (rule.conditionId != null) { 706 out.attribute(null, RULE_ATT_CONDITION_ID, rule.conditionId.toString()); 707 } 708 out.attribute(null, RULE_ATT_CREATION_TIME, Long.toString(rule.creationTime)); 709 if (rule.enabler != null) { 710 out.attribute(null, RULE_ATT_ENABLER, rule.enabler); 711 } 712 if (rule.condition != null) { 713 writeConditionXml(rule.condition, out); 714 } 715 if (rule.zenPolicy != null) { 716 writeZenPolicyXml(rule.zenPolicy, out); 717 } 718 out.attribute(null, RULE_ATT_MODIFIED, Boolean.toString(rule.modified)); 719 } 720 readConditionXml(XmlPullParser parser)721 public static Condition readConditionXml(XmlPullParser parser) { 722 final Uri id = safeUri(parser, CONDITION_ATT_ID); 723 if (id == null) return null; 724 final String summary = parser.getAttributeValue(null, CONDITION_ATT_SUMMARY); 725 final String line1 = parser.getAttributeValue(null, CONDITION_ATT_LINE1); 726 final String line2 = parser.getAttributeValue(null, CONDITION_ATT_LINE2); 727 final int icon = safeInt(parser, CONDITION_ATT_ICON, -1); 728 final int state = safeInt(parser, CONDITION_ATT_STATE, -1); 729 final int flags = safeInt(parser, CONDITION_ATT_FLAGS, -1); 730 try { 731 return new Condition(id, summary, line1, line2, icon, state, flags); 732 } catch (IllegalArgumentException e) { 733 Slog.w(TAG, "Unable to read condition xml", e); 734 return null; 735 } 736 } 737 writeConditionXml(Condition c, XmlSerializer out)738 public static void writeConditionXml(Condition c, XmlSerializer out) throws IOException { 739 out.attribute(null, CONDITION_ATT_ID, c.id.toString()); 740 out.attribute(null, CONDITION_ATT_SUMMARY, c.summary); 741 out.attribute(null, CONDITION_ATT_LINE1, c.line1); 742 out.attribute(null, CONDITION_ATT_LINE2, c.line2); 743 out.attribute(null, CONDITION_ATT_ICON, Integer.toString(c.icon)); 744 out.attribute(null, CONDITION_ATT_STATE, Integer.toString(c.state)); 745 out.attribute(null, CONDITION_ATT_FLAGS, Integer.toString(c.flags)); 746 } 747 748 /** 749 * Read the zen policy from xml 750 * Returns null if no zen policy exists 751 */ readZenPolicyXml(XmlPullParser parser)752 public static ZenPolicy readZenPolicyXml(XmlPullParser parser) { 753 boolean policySet = false; 754 755 ZenPolicy.Builder builder = new ZenPolicy.Builder(); 756 final int calls = safeInt(parser, ALLOW_ATT_CALLS_FROM, ZenPolicy.PEOPLE_TYPE_UNSET); 757 final int messages = safeInt(parser, ALLOW_ATT_MESSAGES_FROM, ZenPolicy.PEOPLE_TYPE_UNSET); 758 final int repeatCallers = safeInt(parser, ALLOW_ATT_REPEAT_CALLERS, ZenPolicy.STATE_UNSET); 759 final int alarms = safeInt(parser, ALLOW_ATT_ALARMS, ZenPolicy.STATE_UNSET); 760 final int media = safeInt(parser, ALLOW_ATT_MEDIA, ZenPolicy.STATE_UNSET); 761 final int system = safeInt(parser, ALLOW_ATT_SYSTEM, ZenPolicy.STATE_UNSET); 762 final int events = safeInt(parser, ALLOW_ATT_EVENTS, ZenPolicy.STATE_UNSET); 763 final int reminders = safeInt(parser, ALLOW_ATT_REMINDERS, ZenPolicy.STATE_UNSET); 764 765 if (calls != ZenPolicy.PEOPLE_TYPE_UNSET) { 766 builder.allowCalls(calls); 767 policySet = true; 768 } 769 if (messages != ZenPolicy.PEOPLE_TYPE_UNSET) { 770 builder.allowMessages(messages); 771 policySet = true; 772 } 773 if (repeatCallers != ZenPolicy.STATE_UNSET) { 774 builder.allowRepeatCallers(repeatCallers == ZenPolicy.STATE_ALLOW); 775 policySet = true; 776 } 777 if (alarms != ZenPolicy.STATE_UNSET) { 778 builder.allowAlarms(alarms == ZenPolicy.STATE_ALLOW); 779 policySet = true; 780 } 781 if (media != ZenPolicy.STATE_UNSET) { 782 builder.allowMedia(media == ZenPolicy.STATE_ALLOW); 783 policySet = true; 784 } 785 if (system != ZenPolicy.STATE_UNSET) { 786 builder.allowSystem(system == ZenPolicy.STATE_ALLOW); 787 policySet = true; 788 } 789 if (events != ZenPolicy.STATE_UNSET) { 790 builder.allowEvents(events == ZenPolicy.STATE_ALLOW); 791 policySet = true; 792 } 793 if (reminders != ZenPolicy.STATE_UNSET) { 794 builder.allowReminders(reminders == ZenPolicy.STATE_ALLOW); 795 policySet = true; 796 } 797 798 final int fullScreenIntent = safeInt(parser, SHOW_ATT_FULL_SCREEN_INTENT, 799 ZenPolicy.STATE_UNSET); 800 final int lights = safeInt(parser, SHOW_ATT_LIGHTS, ZenPolicy.STATE_UNSET); 801 final int peek = safeInt(parser, SHOW_ATT_PEEK, ZenPolicy.STATE_UNSET); 802 final int statusBar = safeInt(parser, SHOW_ATT_STATUS_BAR_ICONS, ZenPolicy.STATE_UNSET); 803 final int badges = safeInt(parser, SHOW_ATT_BADGES, ZenPolicy.STATE_UNSET); 804 final int ambient = safeInt(parser, SHOW_ATT_AMBIENT, ZenPolicy.STATE_UNSET); 805 final int notificationList = safeInt(parser, SHOW_ATT_NOTIFICATION_LIST, 806 ZenPolicy.STATE_UNSET); 807 808 if (fullScreenIntent != ZenPolicy.STATE_UNSET) { 809 builder.showFullScreenIntent(fullScreenIntent == ZenPolicy.STATE_ALLOW); 810 policySet = true; 811 } 812 if (lights != ZenPolicy.STATE_UNSET) { 813 builder.showLights(lights == ZenPolicy.STATE_ALLOW); 814 policySet = true; 815 } 816 if (peek != ZenPolicy.STATE_UNSET) { 817 builder.showPeeking(peek == ZenPolicy.STATE_ALLOW); 818 policySet = true; 819 } 820 if (statusBar != ZenPolicy.STATE_UNSET) { 821 builder.showStatusBarIcons(statusBar == ZenPolicy.STATE_ALLOW); 822 policySet = true; 823 } 824 if (badges != ZenPolicy.STATE_UNSET) { 825 builder.showBadges(badges == ZenPolicy.STATE_ALLOW); 826 policySet = true; 827 } 828 if (ambient != ZenPolicy.STATE_UNSET) { 829 builder.showInAmbientDisplay(ambient == ZenPolicy.STATE_ALLOW); 830 policySet = true; 831 } 832 if (notificationList != ZenPolicy.STATE_UNSET) { 833 builder.showInNotificationList(notificationList == ZenPolicy.STATE_ALLOW); 834 policySet = true; 835 } 836 837 if (policySet) { 838 return builder.build(); 839 } 840 return null; 841 } 842 843 /** 844 * Writes zen policy to xml 845 */ writeZenPolicyXml(ZenPolicy policy, XmlSerializer out)846 public static void writeZenPolicyXml(ZenPolicy policy, XmlSerializer out) 847 throws IOException { 848 writeZenPolicyState(ALLOW_ATT_CALLS_FROM, policy.getPriorityCallSenders(), out); 849 writeZenPolicyState(ALLOW_ATT_MESSAGES_FROM, policy.getPriorityMessageSenders(), out); 850 writeZenPolicyState(ALLOW_ATT_REPEAT_CALLERS, policy.getPriorityCategoryRepeatCallers(), 851 out); 852 writeZenPolicyState(ALLOW_ATT_ALARMS, policy.getPriorityCategoryAlarms(), out); 853 writeZenPolicyState(ALLOW_ATT_MEDIA, policy.getPriorityCategoryMedia(), out); 854 writeZenPolicyState(ALLOW_ATT_SYSTEM, policy.getPriorityCategorySystem(), out); 855 writeZenPolicyState(ALLOW_ATT_REMINDERS, policy.getPriorityCategoryReminders(), out); 856 writeZenPolicyState(ALLOW_ATT_EVENTS, policy.getPriorityCategoryEvents(), out); 857 858 writeZenPolicyState(SHOW_ATT_FULL_SCREEN_INTENT, policy.getVisualEffectFullScreenIntent(), 859 out); 860 writeZenPolicyState(SHOW_ATT_LIGHTS, policy.getVisualEffectLights(), out); 861 writeZenPolicyState(SHOW_ATT_PEEK, policy.getVisualEffectPeek(), out); 862 writeZenPolicyState(SHOW_ATT_STATUS_BAR_ICONS, policy.getVisualEffectStatusBar(), out); 863 writeZenPolicyState(SHOW_ATT_BADGES, policy.getVisualEffectBadge(), out); 864 writeZenPolicyState(SHOW_ATT_AMBIENT, policy.getVisualEffectAmbient(), out); 865 writeZenPolicyState(SHOW_ATT_NOTIFICATION_LIST, policy.getVisualEffectNotificationList(), 866 out); 867 } 868 writeZenPolicyState(String attr, int val, XmlSerializer out)869 private static void writeZenPolicyState(String attr, int val, XmlSerializer out) 870 throws IOException { 871 if (Objects.equals(attr, ALLOW_ATT_CALLS_FROM) 872 || Objects.equals(attr, ALLOW_ATT_MESSAGES_FROM)) { 873 if (val != ZenPolicy.PEOPLE_TYPE_UNSET) { 874 out.attribute(null, attr, Integer.toString(val)); 875 } 876 } else { 877 if (val != ZenPolicy.STATE_UNSET) { 878 out.attribute(null, attr, Integer.toString(val)); 879 } 880 } 881 } 882 isValidHour(int val)883 public static boolean isValidHour(int val) { 884 return val >= 0 && val < 24; 885 } 886 isValidMinute(int val)887 public static boolean isValidMinute(int val) { 888 return val >= 0 && val < 60; 889 } 890 isValidSource(int source)891 private static boolean isValidSource(int source) { 892 return source >= SOURCE_ANYONE && source <= MAX_SOURCE; 893 } 894 unsafeBoolean(XmlPullParser parser, String att)895 private static Boolean unsafeBoolean(XmlPullParser parser, String att) { 896 final String val = parser.getAttributeValue(null, att); 897 if (TextUtils.isEmpty(val)) return null; 898 return Boolean.parseBoolean(val); 899 } 900 safeBoolean(XmlPullParser parser, String att, boolean defValue)901 private static boolean safeBoolean(XmlPullParser parser, String att, boolean defValue) { 902 final String val = parser.getAttributeValue(null, att); 903 return safeBoolean(val, defValue); 904 } 905 safeBoolean(String val, boolean defValue)906 private static boolean safeBoolean(String val, boolean defValue) { 907 if (TextUtils.isEmpty(val)) return defValue; 908 return Boolean.parseBoolean(val); 909 } 910 safeInt(XmlPullParser parser, String att, int defValue)911 private static int safeInt(XmlPullParser parser, String att, int defValue) { 912 final String val = parser.getAttributeValue(null, att); 913 return tryParseInt(val, defValue); 914 } 915 safeComponentName(XmlPullParser parser, String att)916 private static ComponentName safeComponentName(XmlPullParser parser, String att) { 917 final String val = parser.getAttributeValue(null, att); 918 if (TextUtils.isEmpty(val)) return null; 919 return ComponentName.unflattenFromString(val); 920 } 921 safeUri(XmlPullParser parser, String att)922 private static Uri safeUri(XmlPullParser parser, String att) { 923 final String val = parser.getAttributeValue(null, att); 924 if (TextUtils.isEmpty(val)) return null; 925 return Uri.parse(val); 926 } 927 safeLong(XmlPullParser parser, String att, long defValue)928 private static long safeLong(XmlPullParser parser, String att, long defValue) { 929 final String val = parser.getAttributeValue(null, att); 930 return tryParseLong(val, defValue); 931 } 932 933 @Override describeContents()934 public int describeContents() { 935 return 0; 936 } 937 copy()938 public ZenModeConfig copy() { 939 final Parcel parcel = Parcel.obtain(); 940 try { 941 writeToParcel(parcel, 0); 942 parcel.setDataPosition(0); 943 return new ZenModeConfig(parcel); 944 } finally { 945 parcel.recycle(); 946 } 947 } 948 949 public static final @android.annotation.NonNull Parcelable.Creator<ZenModeConfig> CREATOR 950 = new Parcelable.Creator<ZenModeConfig>() { 951 @Override 952 public ZenModeConfig createFromParcel(Parcel source) { 953 return new ZenModeConfig(source); 954 } 955 956 @Override 957 public ZenModeConfig[] newArray(int size) { 958 return new ZenModeConfig[size]; 959 } 960 }; 961 962 /** 963 * Converts a ZenModeConfig to a ZenPolicy 964 */ toZenPolicy()965 public ZenPolicy toZenPolicy() { 966 ZenPolicy.Builder builder = new ZenPolicy.Builder() 967 .allowCalls(allowCalls 968 ? ZenModeConfig.getZenPolicySenders(allowCallsFrom) 969 : ZenPolicy.PEOPLE_TYPE_NONE) 970 .allowRepeatCallers(allowRepeatCallers) 971 .allowMessages(allowMessages 972 ? ZenModeConfig.getZenPolicySenders(allowMessagesFrom) 973 : ZenPolicy.PEOPLE_TYPE_NONE) 974 .allowReminders(allowReminders) 975 .allowEvents(allowEvents) 976 .allowAlarms(allowAlarms) 977 .allowMedia(allowMedia) 978 .allowSystem(allowSystem) 979 .allowConversations(allowConversations 980 ? ZenModeConfig.getZenPolicySenders(allowConversationsFrom) 981 : ZenPolicy.PEOPLE_TYPE_NONE); 982 if (suppressedVisualEffects == 0) { 983 builder.showAllVisualEffects(); 984 } else { 985 // configs don't have an unset state: wither true or false. 986 builder.showFullScreenIntent( 987 (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT) == 0); 988 builder.showLights( 989 (suppressedVisualEffects & SUPPRESSED_EFFECT_LIGHTS) == 0); 990 builder.showPeeking( 991 (suppressedVisualEffects & SUPPRESSED_EFFECT_PEEK) == 0); 992 builder.showStatusBarIcons( 993 (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_STATUS_BAR) == 0); 994 builder.showBadges( 995 (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_BADGE) == 0); 996 builder.showInAmbientDisplay( 997 (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_AMBIENT) == 0); 998 builder.showInNotificationList( 999 (suppressedVisualEffects & Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST) == 0); 1000 } 1001 return builder.build(); 1002 } 1003 1004 /** 1005 * Converts a zenPolicy to a notificationPolicy using this ZenModeConfig's values as its 1006 * defaults for all unset values in zenPolicy 1007 */ toNotificationPolicy(ZenPolicy zenPolicy)1008 public Policy toNotificationPolicy(ZenPolicy zenPolicy) { 1009 NotificationManager.Policy defaultPolicy = toNotificationPolicy(); 1010 int priorityCategories = 0; 1011 int suppressedVisualEffects = 0; 1012 int callSenders = defaultPolicy.priorityCallSenders; 1013 int messageSenders = defaultPolicy.priorityMessageSenders; 1014 int conversationSenders = defaultPolicy.priorityConversationSenders; 1015 1016 if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_REMINDERS, 1017 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_REMINDERS, defaultPolicy))) { 1018 priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS; 1019 } 1020 1021 if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_EVENTS, 1022 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_EVENTS, defaultPolicy))) { 1023 priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS; 1024 } 1025 1026 if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_MESSAGES, 1027 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_MESSAGES, defaultPolicy))) { 1028 priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES; 1029 messageSenders = getNotificationPolicySenders(zenPolicy.getPriorityMessageSenders(), 1030 messageSenders); 1031 } 1032 1033 if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_CONVERSATIONS, 1034 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_CONVERSATIONS, defaultPolicy))) { 1035 priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS; 1036 conversationSenders = getNotificationPolicySenders( 1037 zenPolicy.getPriorityConversationSenders(), 1038 conversationSenders); 1039 } 1040 1041 if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_CALLS, 1042 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_CALLS, defaultPolicy))) { 1043 priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS; 1044 callSenders = getNotificationPolicySenders(zenPolicy.getPriorityCallSenders(), 1045 callSenders); 1046 } 1047 1048 if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS, 1049 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_REPEAT_CALLERS, 1050 defaultPolicy))) { 1051 priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS; 1052 } 1053 1054 if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_ALARMS, 1055 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_ALARMS, defaultPolicy))) { 1056 priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS; 1057 } 1058 1059 if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_MEDIA, 1060 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_MEDIA, defaultPolicy))) { 1061 priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA; 1062 } 1063 1064 if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_SYSTEM, 1065 isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_SYSTEM, defaultPolicy))) { 1066 priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM; 1067 } 1068 1069 boolean suppressFullScreenIntent = !zenPolicy.isVisualEffectAllowed( 1070 ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT, 1071 isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT, 1072 defaultPolicy)); 1073 1074 boolean suppressLights = !zenPolicy.isVisualEffectAllowed( 1075 ZenPolicy.VISUAL_EFFECT_LIGHTS, 1076 isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_LIGHTS, 1077 defaultPolicy)); 1078 1079 boolean suppressAmbient = !zenPolicy.isVisualEffectAllowed( 1080 ZenPolicy.VISUAL_EFFECT_AMBIENT, 1081 isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_AMBIENT, 1082 defaultPolicy)); 1083 1084 if (suppressFullScreenIntent && suppressLights && suppressAmbient) { 1085 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_OFF; 1086 } 1087 1088 if (suppressFullScreenIntent) { 1089 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; 1090 } 1091 1092 if (suppressLights) { 1093 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS; 1094 } 1095 1096 if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_PEEK, 1097 isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_PEEK, 1098 defaultPolicy))) { 1099 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_PEEK; 1100 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_ON; 1101 } 1102 1103 if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_STATUS_BAR, 1104 isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_STATUS_BAR, 1105 defaultPolicy))) { 1106 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_STATUS_BAR; 1107 } 1108 1109 if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_BADGE, 1110 isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_BADGE, 1111 defaultPolicy))) { 1112 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE; 1113 } 1114 1115 if (suppressAmbient) { 1116 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_AMBIENT; 1117 } 1118 1119 if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST, 1120 isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST, 1121 defaultPolicy))) { 1122 suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST; 1123 } 1124 1125 return new NotificationManager.Policy(priorityCategories, callSenders, 1126 messageSenders, suppressedVisualEffects, defaultPolicy.state, conversationSenders); 1127 } 1128 isPriorityCategoryEnabled(int categoryType, Policy policy)1129 private boolean isPriorityCategoryEnabled(int categoryType, Policy policy) { 1130 return (policy.priorityCategories & categoryType) != 0; 1131 } 1132 isVisualEffectAllowed(int visualEffect, Policy policy)1133 private boolean isVisualEffectAllowed(int visualEffect, Policy policy) { 1134 return (policy.suppressedVisualEffects & visualEffect) == 0; 1135 } 1136 getNotificationPolicySenders(@enPolicy.PeopleType int senders, int defaultPolicySender)1137 private int getNotificationPolicySenders(@ZenPolicy.PeopleType int senders, 1138 int defaultPolicySender) { 1139 switch (senders) { 1140 case ZenPolicy.PEOPLE_TYPE_ANYONE: 1141 return Policy.PRIORITY_SENDERS_ANY; 1142 case ZenPolicy.PEOPLE_TYPE_CONTACTS: 1143 return Policy.PRIORITY_SENDERS_CONTACTS; 1144 case ZenPolicy.PEOPLE_TYPE_STARRED: 1145 return Policy.PRIORITY_SENDERS_STARRED; 1146 default: 1147 return defaultPolicySender; 1148 } 1149 } 1150 1151 1152 /** 1153 * Maps NotificationManager.Policy senders type to ZenPolicy.PeopleType 1154 */ getZenPolicySenders(int senders)1155 public static @ZenPolicy.PeopleType int getZenPolicySenders(int senders) { 1156 switch (senders) { 1157 case Policy.PRIORITY_SENDERS_ANY: 1158 return ZenPolicy.PEOPLE_TYPE_ANYONE; 1159 case Policy.PRIORITY_SENDERS_CONTACTS: 1160 return ZenPolicy.PEOPLE_TYPE_CONTACTS; 1161 case Policy.PRIORITY_SENDERS_STARRED: 1162 default: 1163 return ZenPolicy.PEOPLE_TYPE_STARRED; 1164 } 1165 } 1166 toNotificationPolicy()1167 public Policy toNotificationPolicy() { 1168 int priorityCategories = 0; 1169 int priorityCallSenders = Policy.PRIORITY_SENDERS_CONTACTS; 1170 int priorityMessageSenders = Policy.PRIORITY_SENDERS_CONTACTS; 1171 int priorityConversationSenders = Policy.CONVERSATION_SENDERS_IMPORTANT; 1172 if (allowConversations) { 1173 priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS; 1174 } 1175 if (allowCalls) { 1176 priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS; 1177 } 1178 if (allowMessages) { 1179 priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES; 1180 } 1181 if (allowEvents) { 1182 priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS; 1183 } 1184 if (allowReminders) { 1185 priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS; 1186 } 1187 if (allowRepeatCallers) { 1188 priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS; 1189 } 1190 if (allowAlarms) { 1191 priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS; 1192 } 1193 if (allowMedia) { 1194 priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA; 1195 } 1196 if (allowSystem) { 1197 priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM; 1198 } 1199 priorityCallSenders = sourceToPrioritySenders(allowCallsFrom, priorityCallSenders); 1200 priorityMessageSenders = sourceToPrioritySenders(allowMessagesFrom, priorityMessageSenders); 1201 priorityConversationSenders = allowConversationsFrom; 1202 1203 return new Policy(priorityCategories, priorityCallSenders, priorityMessageSenders, 1204 suppressedVisualEffects, areChannelsBypassingDnd 1205 ? Policy.STATE_CHANNELS_BYPASSING_DND : 0, 1206 priorityConversationSenders); 1207 } 1208 1209 /** 1210 * Creates scheduleCalendar from a condition id 1211 * @param conditionId 1212 * @return ScheduleCalendar with info populated with conditionId 1213 */ toScheduleCalendar(Uri conditionId)1214 public static ScheduleCalendar toScheduleCalendar(Uri conditionId) { 1215 final ScheduleInfo schedule = ZenModeConfig.tryParseScheduleConditionId(conditionId); 1216 if (schedule == null || schedule.days == null || schedule.days.length == 0) return null; 1217 final ScheduleCalendar sc = new ScheduleCalendar(); 1218 sc.setSchedule(schedule); 1219 sc.setTimeZone(TimeZone.getDefault()); 1220 return sc; 1221 } 1222 sourceToPrioritySenders(int source, int def)1223 private static int sourceToPrioritySenders(int source, int def) { 1224 switch (source) { 1225 case SOURCE_ANYONE: return Policy.PRIORITY_SENDERS_ANY; 1226 case SOURCE_CONTACT: return Policy.PRIORITY_SENDERS_CONTACTS; 1227 case SOURCE_STAR: return Policy.PRIORITY_SENDERS_STARRED; 1228 default: return def; 1229 } 1230 } 1231 prioritySendersToSource(int prioritySenders, int def)1232 private static int prioritySendersToSource(int prioritySenders, int def) { 1233 switch (prioritySenders) { 1234 case Policy.PRIORITY_SENDERS_CONTACTS: return SOURCE_CONTACT; 1235 case Policy.PRIORITY_SENDERS_STARRED: return SOURCE_STAR; 1236 case Policy.PRIORITY_SENDERS_ANY: return SOURCE_ANYONE; 1237 default: return def; 1238 } 1239 } 1240 normalizePrioritySenders(int prioritySenders, int def)1241 private static int normalizePrioritySenders(int prioritySenders, int def) { 1242 if (!(prioritySenders == Policy.PRIORITY_SENDERS_CONTACTS 1243 || prioritySenders == Policy.PRIORITY_SENDERS_STARRED 1244 || prioritySenders == Policy.PRIORITY_SENDERS_ANY)) { 1245 return def; 1246 } 1247 return prioritySenders; 1248 } 1249 normalizeConversationSenders(boolean allowed, int senders, int def)1250 private static int normalizeConversationSenders(boolean allowed, int senders, int def) { 1251 if (!allowed) { 1252 return CONVERSATION_SENDERS_NONE; 1253 } 1254 if (!(senders == CONVERSATION_SENDERS_ANYONE 1255 || senders == CONVERSATION_SENDERS_IMPORTANT 1256 || senders == CONVERSATION_SENDERS_NONE)) { 1257 return def; 1258 } 1259 return senders; 1260 } 1261 applyNotificationPolicy(Policy policy)1262 public void applyNotificationPolicy(Policy policy) { 1263 if (policy == null) return; 1264 allowAlarms = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS) != 0; 1265 allowMedia = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MEDIA) != 0; 1266 allowSystem = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_SYSTEM) != 0; 1267 allowEvents = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_EVENTS) != 0; 1268 allowReminders = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REMINDERS) != 0; 1269 allowCalls = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_CALLS) != 0; 1270 allowMessages = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MESSAGES) != 0; 1271 allowRepeatCallers = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REPEAT_CALLERS) 1272 != 0; 1273 allowCallsFrom = normalizePrioritySenders(policy.priorityCallSenders, allowCallsFrom); 1274 allowMessagesFrom = normalizePrioritySenders(policy.priorityMessageSenders, 1275 allowMessagesFrom); 1276 if (policy.suppressedVisualEffects != Policy.SUPPRESSED_EFFECTS_UNSET) { 1277 suppressedVisualEffects = policy.suppressedVisualEffects; 1278 } 1279 allowConversations = (policy.priorityCategories 1280 & Policy.PRIORITY_CATEGORY_CONVERSATIONS) != 0; 1281 allowConversationsFrom = normalizeConversationSenders(allowConversations, 1282 policy.priorityConversationSenders, 1283 allowConversationsFrom); 1284 if (policy.state != Policy.STATE_UNSET) { 1285 areChannelsBypassingDnd = (policy.state & Policy.STATE_CHANNELS_BYPASSING_DND) != 0; 1286 } 1287 } 1288 toTimeCondition(Context context, int minutesFromNow, int userHandle)1289 public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle) { 1290 return toTimeCondition(context, minutesFromNow, userHandle, false /*shortVersion*/); 1291 } 1292 toTimeCondition(Context context, int minutesFromNow, int userHandle, boolean shortVersion)1293 public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle, 1294 boolean shortVersion) { 1295 final long now = System.currentTimeMillis(); 1296 final long millis = minutesFromNow == 0 ? ZERO_VALUE_MS : minutesFromNow * MINUTES_MS; 1297 return toTimeCondition(context, now + millis, minutesFromNow, userHandle, shortVersion); 1298 } 1299 toTimeCondition(Context context, long time, int minutes, int userHandle, boolean shortVersion)1300 public static Condition toTimeCondition(Context context, long time, int minutes, 1301 int userHandle, boolean shortVersion) { 1302 final int num; 1303 String summary, line1, line2; 1304 final CharSequence formattedTime = 1305 getFormattedTime(context, time, isToday(time), userHandle); 1306 final Resources res = context.getResources(); 1307 if (minutes < 60) { 1308 // display as minutes 1309 num = minutes; 1310 int summaryResId = shortVersion ? R.plurals.zen_mode_duration_minutes_summary_short 1311 : R.plurals.zen_mode_duration_minutes_summary; 1312 summary = res.getQuantityString(summaryResId, num, num, formattedTime); 1313 int line1ResId = shortVersion ? R.plurals.zen_mode_duration_minutes_short 1314 : R.plurals.zen_mode_duration_minutes; 1315 line1 = res.getQuantityString(line1ResId, num, num, formattedTime); 1316 line2 = res.getString(R.string.zen_mode_until, formattedTime); 1317 } else if (minutes < DAY_MINUTES) { 1318 // display as hours 1319 num = Math.round(minutes / 60f); 1320 int summaryResId = shortVersion ? R.plurals.zen_mode_duration_hours_summary_short 1321 : R.plurals.zen_mode_duration_hours_summary; 1322 summary = res.getQuantityString(summaryResId, num, num, formattedTime); 1323 int line1ResId = shortVersion ? R.plurals.zen_mode_duration_hours_short 1324 : R.plurals.zen_mode_duration_hours; 1325 line1 = res.getQuantityString(line1ResId, num, num, formattedTime); 1326 line2 = res.getString(R.string.zen_mode_until, formattedTime); 1327 } else { 1328 // display as day/time 1329 summary = line1 = line2 = res.getString(R.string.zen_mode_until, formattedTime); 1330 } 1331 final Uri id = toCountdownConditionId(time, false); 1332 return new Condition(id, summary, line1, line2, 0, Condition.STATE_TRUE, 1333 Condition.FLAG_RELEVANT_NOW); 1334 } 1335 1336 /** 1337 * Converts countdown to alarm parameters into a condition with user facing summary 1338 */ toNextAlarmCondition(Context context, long alarm, int userHandle)1339 public static Condition toNextAlarmCondition(Context context, long alarm, 1340 int userHandle) { 1341 boolean isSameDay = isToday(alarm); 1342 final CharSequence formattedTime = getFormattedTime(context, alarm, isSameDay, userHandle); 1343 final Resources res = context.getResources(); 1344 final String line1 = res.getString(R.string.zen_mode_until, formattedTime); 1345 final Uri id = toCountdownConditionId(alarm, true); 1346 return new Condition(id, "", line1, "", 0, Condition.STATE_TRUE, 1347 Condition.FLAG_RELEVANT_NOW); 1348 } 1349 1350 /** 1351 * Creates readable time from time in milliseconds 1352 */ getFormattedTime(Context context, long time, boolean isSameDay, int userHandle)1353 public static CharSequence getFormattedTime(Context context, long time, boolean isSameDay, 1354 int userHandle) { 1355 String skeleton = (!isSameDay ? "EEE " : "") 1356 + (DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma"); 1357 final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton); 1358 return DateFormat.format(pattern, time); 1359 } 1360 1361 /** 1362 * Determines whether a time in milliseconds is today or not 1363 */ isToday(long time)1364 public static boolean isToday(long time) { 1365 GregorianCalendar now = new GregorianCalendar(); 1366 GregorianCalendar endTime = new GregorianCalendar(); 1367 endTime.setTimeInMillis(time); 1368 if (now.get(Calendar.YEAR) == endTime.get(Calendar.YEAR) 1369 && now.get(Calendar.MONTH) == endTime.get(Calendar.MONTH) 1370 && now.get(Calendar.DATE) == endTime.get(Calendar.DATE)) { 1371 return true; 1372 } 1373 return false; 1374 } 1375 1376 // ==== Built-in system conditions ==== 1377 1378 public static final String SYSTEM_AUTHORITY = "android"; 1379 1380 // ==== Built-in system condition: countdown ==== 1381 1382 public static final String COUNTDOWN_PATH = "countdown"; 1383 1384 public static final String IS_ALARM_PATH = "alarm"; 1385 1386 /** 1387 * Converts countdown condition parameters into a condition id. 1388 */ toCountdownConditionId(long time, boolean alarm)1389 public static Uri toCountdownConditionId(long time, boolean alarm) { 1390 return new Uri.Builder().scheme(Condition.SCHEME) 1391 .authority(SYSTEM_AUTHORITY) 1392 .appendPath(COUNTDOWN_PATH) 1393 .appendPath(Long.toString(time)) 1394 .appendPath(IS_ALARM_PATH) 1395 .appendPath(Boolean.toString(alarm)) 1396 .build(); 1397 } 1398 tryParseCountdownConditionId(Uri conditionId)1399 public static long tryParseCountdownConditionId(Uri conditionId) { 1400 if (!Condition.isValidId(conditionId, SYSTEM_AUTHORITY)) return 0; 1401 if (conditionId.getPathSegments().size() < 2 1402 || !COUNTDOWN_PATH.equals(conditionId.getPathSegments().get(0))) return 0; 1403 try { 1404 return Long.parseLong(conditionId.getPathSegments().get(1)); 1405 } catch (RuntimeException e) { 1406 Slog.w(TAG, "Error parsing countdown condition: " + conditionId, e); 1407 return 0; 1408 } 1409 } 1410 1411 /** 1412 * Returns whether this condition is a countdown condition. 1413 */ isValidCountdownConditionId(Uri conditionId)1414 public static boolean isValidCountdownConditionId(Uri conditionId) { 1415 return tryParseCountdownConditionId(conditionId) != 0; 1416 } 1417 1418 /** 1419 * Returns whether this condition is a countdown to an alarm. 1420 */ isValidCountdownToAlarmConditionId(Uri conditionId)1421 public static boolean isValidCountdownToAlarmConditionId(Uri conditionId) { 1422 if (tryParseCountdownConditionId(conditionId) != 0) { 1423 if (conditionId.getPathSegments().size() < 4 1424 || !IS_ALARM_PATH.equals(conditionId.getPathSegments().get(2))) { 1425 return false; 1426 } 1427 try { 1428 return Boolean.parseBoolean(conditionId.getPathSegments().get(3)); 1429 } catch (RuntimeException e) { 1430 Slog.w(TAG, "Error parsing countdown alarm condition: " + conditionId, e); 1431 return false; 1432 } 1433 } 1434 return false; 1435 } 1436 1437 // ==== Built-in system condition: schedule ==== 1438 1439 public static final String SCHEDULE_PATH = "schedule"; 1440 toScheduleConditionId(ScheduleInfo schedule)1441 public static Uri toScheduleConditionId(ScheduleInfo schedule) { 1442 return new Uri.Builder().scheme(Condition.SCHEME) 1443 .authority(SYSTEM_AUTHORITY) 1444 .appendPath(SCHEDULE_PATH) 1445 .appendQueryParameter("days", toDayList(schedule.days)) 1446 .appendQueryParameter("start", schedule.startHour + "." + schedule.startMinute) 1447 .appendQueryParameter("end", schedule.endHour + "." + schedule.endMinute) 1448 .appendQueryParameter("exitAtAlarm", String.valueOf(schedule.exitAtAlarm)) 1449 .build(); 1450 } 1451 isValidScheduleConditionId(Uri conditionId)1452 public static boolean isValidScheduleConditionId(Uri conditionId) { 1453 ScheduleInfo info; 1454 try { 1455 info = tryParseScheduleConditionId(conditionId); 1456 } catch (NullPointerException | ArrayIndexOutOfBoundsException e) { 1457 return false; 1458 } 1459 1460 if (info == null || info.days == null || info.days.length == 0) { 1461 return false; 1462 } 1463 return true; 1464 } 1465 1466 /** 1467 * Returns whether the conditionId is a valid ScheduleCondition. 1468 * If allowNever is true, this will return true even if the ScheduleCondition never occurs. 1469 */ isValidScheduleConditionId(Uri conditionId, boolean allowNever)1470 public static boolean isValidScheduleConditionId(Uri conditionId, boolean allowNever) { 1471 ScheduleInfo info; 1472 try { 1473 info = tryParseScheduleConditionId(conditionId); 1474 } catch (NullPointerException | ArrayIndexOutOfBoundsException e) { 1475 return false; 1476 } 1477 1478 if (info == null || (!allowNever && (info.days == null || info.days.length == 0))) { 1479 return false; 1480 } 1481 return true; 1482 } 1483 1484 @UnsupportedAppUsage tryParseScheduleConditionId(Uri conditionId)1485 public static ScheduleInfo tryParseScheduleConditionId(Uri conditionId) { 1486 final boolean isSchedule = conditionId != null 1487 && Condition.SCHEME.equals(conditionId.getScheme()) 1488 && ZenModeConfig.SYSTEM_AUTHORITY.equals(conditionId.getAuthority()) 1489 && conditionId.getPathSegments().size() == 1 1490 && ZenModeConfig.SCHEDULE_PATH.equals(conditionId.getPathSegments().get(0)); 1491 if (!isSchedule) return null; 1492 final int[] start = tryParseHourAndMinute(conditionId.getQueryParameter("start")); 1493 final int[] end = tryParseHourAndMinute(conditionId.getQueryParameter("end")); 1494 if (start == null || end == null) return null; 1495 final ScheduleInfo rt = new ScheduleInfo(); 1496 rt.days = tryParseDayList(conditionId.getQueryParameter("days"), "\\."); 1497 rt.startHour = start[0]; 1498 rt.startMinute = start[1]; 1499 rt.endHour = end[0]; 1500 rt.endMinute = end[1]; 1501 rt.exitAtAlarm = safeBoolean(conditionId.getQueryParameter("exitAtAlarm"), false); 1502 return rt; 1503 } 1504 getScheduleConditionProvider()1505 public static ComponentName getScheduleConditionProvider() { 1506 return new ComponentName(SYSTEM_AUTHORITY, "ScheduleConditionProvider"); 1507 } 1508 1509 public static class ScheduleInfo { 1510 @UnsupportedAppUsage 1511 public int[] days; 1512 @UnsupportedAppUsage 1513 public int startHour; 1514 @UnsupportedAppUsage 1515 public int startMinute; 1516 @UnsupportedAppUsage 1517 public int endHour; 1518 @UnsupportedAppUsage 1519 public int endMinute; 1520 public boolean exitAtAlarm; 1521 public long nextAlarm; 1522 1523 @Override hashCode()1524 public int hashCode() { 1525 return 0; 1526 } 1527 1528 @Override equals(Object o)1529 public boolean equals(Object o) { 1530 if (!(o instanceof ScheduleInfo)) return false; 1531 final ScheduleInfo other = (ScheduleInfo) o; 1532 return toDayList(days).equals(toDayList(other.days)) 1533 && startHour == other.startHour 1534 && startMinute == other.startMinute 1535 && endHour == other.endHour 1536 && endMinute == other.endMinute 1537 && exitAtAlarm == other.exitAtAlarm; 1538 } 1539 copy()1540 public ScheduleInfo copy() { 1541 final ScheduleInfo rt = new ScheduleInfo(); 1542 if (days != null) { 1543 rt.days = new int[days.length]; 1544 System.arraycopy(days, 0, rt.days, 0, days.length); 1545 } 1546 rt.startHour = startHour; 1547 rt.startMinute = startMinute; 1548 rt.endHour = endHour; 1549 rt.endMinute = endMinute; 1550 rt.exitAtAlarm = exitAtAlarm; 1551 rt.nextAlarm = nextAlarm; 1552 return rt; 1553 } 1554 1555 @Override toString()1556 public String toString() { 1557 return "ScheduleInfo{" + 1558 "days=" + Arrays.toString(days) + 1559 ", startHour=" + startHour + 1560 ", startMinute=" + startMinute + 1561 ", endHour=" + endHour + 1562 ", endMinute=" + endMinute + 1563 ", exitAtAlarm=" + exitAtAlarm + 1564 ", nextAlarm=" + ts(nextAlarm) + 1565 '}'; 1566 } 1567 ts(long time)1568 protected static String ts(long time) { 1569 return new Date(time) + " (" + time + ")"; 1570 } 1571 } 1572 1573 // ==== Built-in system condition: event ==== 1574 1575 public static final String EVENT_PATH = "event"; 1576 toEventConditionId(EventInfo event)1577 public static Uri toEventConditionId(EventInfo event) { 1578 return new Uri.Builder().scheme(Condition.SCHEME) 1579 .authority(SYSTEM_AUTHORITY) 1580 .appendPath(EVENT_PATH) 1581 .appendQueryParameter("userId", Long.toString(event.userId)) 1582 .appendQueryParameter("calendar", event.calName != null ? event.calName : "") 1583 .appendQueryParameter("calendarId", event.calendarId != null 1584 ? event.calendarId.toString() : "") 1585 .appendQueryParameter("reply", Integer.toString(event.reply)) 1586 .build(); 1587 } 1588 isValidEventConditionId(Uri conditionId)1589 public static boolean isValidEventConditionId(Uri conditionId) { 1590 return tryParseEventConditionId(conditionId) != null; 1591 } 1592 tryParseEventConditionId(Uri conditionId)1593 public static EventInfo tryParseEventConditionId(Uri conditionId) { 1594 final boolean isEvent = conditionId != null 1595 && Condition.SCHEME.equals(conditionId.getScheme()) 1596 && ZenModeConfig.SYSTEM_AUTHORITY.equals(conditionId.getAuthority()) 1597 && conditionId.getPathSegments().size() == 1 1598 && EVENT_PATH.equals(conditionId.getPathSegments().get(0)); 1599 if (!isEvent) return null; 1600 final EventInfo rt = new EventInfo(); 1601 rt.userId = tryParseInt(conditionId.getQueryParameter("userId"), UserHandle.USER_NULL); 1602 rt.calName = conditionId.getQueryParameter("calendar"); 1603 if (TextUtils.isEmpty(rt.calName)) { 1604 rt.calName = null; 1605 } 1606 rt.calendarId = tryParseLong(conditionId.getQueryParameter("calendarId"), null); 1607 rt.reply = tryParseInt(conditionId.getQueryParameter("reply"), 0); 1608 return rt; 1609 } 1610 getEventConditionProvider()1611 public static ComponentName getEventConditionProvider() { 1612 return new ComponentName(SYSTEM_AUTHORITY, "EventConditionProvider"); 1613 } 1614 1615 public static class EventInfo { 1616 public static final int REPLY_ANY_EXCEPT_NO = 0; 1617 public static final int REPLY_YES_OR_MAYBE = 1; 1618 public static final int REPLY_YES = 2; 1619 1620 public int userId = UserHandle.USER_NULL; // USER_NULL = unspecified - use current user 1621 public String calName; // CalendarContract.Calendars.DISPLAY_NAME, or null for any 1622 public Long calendarId; // Calendars._ID, or null if restored from < Q calendar 1623 public int reply; 1624 1625 @Override hashCode()1626 public int hashCode() { 1627 return Objects.hash(userId, calName, calendarId, reply); 1628 } 1629 1630 @Override equals(Object o)1631 public boolean equals(Object o) { 1632 if (!(o instanceof EventInfo)) return false; 1633 final EventInfo other = (EventInfo) o; 1634 return userId == other.userId 1635 && Objects.equals(calName, other.calName) 1636 && reply == other.reply 1637 && Objects.equals(calendarId, other.calendarId); 1638 } 1639 copy()1640 public EventInfo copy() { 1641 final EventInfo rt = new EventInfo(); 1642 rt.userId = userId; 1643 rt.calName = calName; 1644 rt.reply = reply; 1645 rt.calendarId = calendarId; 1646 return rt; 1647 } 1648 resolveUserId(int userId)1649 public static int resolveUserId(int userId) { 1650 return userId == UserHandle.USER_NULL ? ActivityManager.getCurrentUser() : userId; 1651 } 1652 } 1653 1654 // ==== End built-in system conditions ==== 1655 tryParseHourAndMinute(String value)1656 private static int[] tryParseHourAndMinute(String value) { 1657 if (TextUtils.isEmpty(value)) return null; 1658 final int i = value.indexOf('.'); 1659 if (i < 1 || i >= value.length() - 1) return null; 1660 final int hour = tryParseInt(value.substring(0, i), -1); 1661 final int minute = tryParseInt(value.substring(i + 1), -1); 1662 return isValidHour(hour) && isValidMinute(minute) ? new int[] { hour, minute } : null; 1663 } 1664 tryParseZenMode(String value, int defValue)1665 private static int tryParseZenMode(String value, int defValue) { 1666 final int rt = tryParseInt(value, defValue); 1667 return Global.isValidZenMode(rt) ? rt : defValue; 1668 } 1669 newRuleId()1670 public static String newRuleId() { 1671 return UUID.randomUUID().toString().replace("-", ""); 1672 } 1673 1674 /** 1675 * Gets the name of the app associated with owner 1676 */ getOwnerCaption(Context context, String owner)1677 public static String getOwnerCaption(Context context, String owner) { 1678 final PackageManager pm = context.getPackageManager(); 1679 try { 1680 final ApplicationInfo info = pm.getApplicationInfo(owner, 0); 1681 if (info != null) { 1682 final CharSequence seq = info.loadLabel(pm); 1683 if (seq != null) { 1684 final String str = seq.toString().trim(); 1685 if (str.length() > 0) { 1686 return str; 1687 } 1688 } 1689 } 1690 } catch (Throwable e) { 1691 Slog.w(TAG, "Error loading owner caption", e); 1692 } 1693 return ""; 1694 } 1695 getConditionSummary(Context context, ZenModeConfig config, int userHandle, boolean shortVersion)1696 public static String getConditionSummary(Context context, ZenModeConfig config, 1697 int userHandle, boolean shortVersion) { 1698 return getConditionLine(context, config, userHandle, false /*useLine1*/, shortVersion); 1699 } 1700 getConditionLine(Context context, ZenModeConfig config, int userHandle, boolean useLine1, boolean shortVersion)1701 private static String getConditionLine(Context context, ZenModeConfig config, 1702 int userHandle, boolean useLine1, boolean shortVersion) { 1703 if (config == null) return ""; 1704 String summary = ""; 1705 if (config.manualRule != null) { 1706 final Uri id = config.manualRule.conditionId; 1707 if (config.manualRule.enabler != null) { 1708 summary = getOwnerCaption(context, config.manualRule.enabler); 1709 } else { 1710 if (id == null) { 1711 summary = context.getString(com.android.internal.R.string.zen_mode_forever); 1712 } else { 1713 final long time = tryParseCountdownConditionId(id); 1714 Condition c = config.manualRule.condition; 1715 if (time > 0) { 1716 final long now = System.currentTimeMillis(); 1717 final long span = time - now; 1718 c = toTimeCondition(context, time, Math.round(span / (float) MINUTES_MS), 1719 userHandle, shortVersion); 1720 } 1721 final String rt = c == null ? "" : useLine1 ? c.line1 : c.summary; 1722 summary = TextUtils.isEmpty(rt) ? "" : rt; 1723 } 1724 } 1725 } 1726 for (ZenRule automaticRule : config.automaticRules.values()) { 1727 if (automaticRule.isAutomaticActive()) { 1728 if (summary.isEmpty()) { 1729 summary = automaticRule.name; 1730 } else { 1731 summary = context.getResources() 1732 .getString(R.string.zen_mode_rule_name_combination, summary, 1733 automaticRule.name); 1734 } 1735 1736 } 1737 } 1738 return summary; 1739 } 1740 1741 public static class ZenRule implements Parcelable { 1742 @UnsupportedAppUsage 1743 public boolean enabled; 1744 @UnsupportedAppUsage 1745 public boolean snoozing; // user manually disabled this instance 1746 @UnsupportedAppUsage 1747 public String name; // required for automatic 1748 @UnsupportedAppUsage 1749 public int zenMode; // ie: Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS 1750 @UnsupportedAppUsage 1751 public Uri conditionId; // required for automatic 1752 public Condition condition; // optional 1753 public ComponentName component; // optional 1754 public ComponentName configurationActivity; // optional 1755 public String id; // required for automatic (unique) 1756 @UnsupportedAppUsage 1757 public long creationTime; // required for automatic 1758 // package name, only used for manual rules when they have turned DND on. 1759 public String enabler; 1760 public ZenPolicy zenPolicy; 1761 public boolean modified; // rule has been modified from initial creation 1762 public String pkg; 1763 ZenRule()1764 public ZenRule() { } 1765 ZenRule(Parcel source)1766 public ZenRule(Parcel source) { 1767 enabled = source.readInt() == 1; 1768 snoozing = source.readInt() == 1; 1769 if (source.readInt() == 1) { 1770 name = source.readString(); 1771 } 1772 zenMode = source.readInt(); 1773 conditionId = source.readParcelable(null); 1774 condition = source.readParcelable(null); 1775 component = source.readParcelable(null); 1776 configurationActivity = source.readParcelable(null); 1777 if (source.readInt() == 1) { 1778 id = source.readString(); 1779 } 1780 creationTime = source.readLong(); 1781 if (source.readInt() == 1) { 1782 enabler = source.readString(); 1783 } 1784 zenPolicy = source.readParcelable(null); 1785 modified = source.readInt() == 1; 1786 pkg = source.readString(); 1787 } 1788 1789 @Override describeContents()1790 public int describeContents() { 1791 return 0; 1792 } 1793 1794 @Override writeToParcel(Parcel dest, int flags)1795 public void writeToParcel(Parcel dest, int flags) { 1796 dest.writeInt(enabled ? 1 : 0); 1797 dest.writeInt(snoozing ? 1 : 0); 1798 if (name != null) { 1799 dest.writeInt(1); 1800 dest.writeString(name); 1801 } else { 1802 dest.writeInt(0); 1803 } 1804 dest.writeInt(zenMode); 1805 dest.writeParcelable(conditionId, 0); 1806 dest.writeParcelable(condition, 0); 1807 dest.writeParcelable(component, 0); 1808 dest.writeParcelable(configurationActivity, 0); 1809 if (id != null) { 1810 dest.writeInt(1); 1811 dest.writeString(id); 1812 } else { 1813 dest.writeInt(0); 1814 } 1815 dest.writeLong(creationTime); 1816 if (enabler != null) { 1817 dest.writeInt(1); 1818 dest.writeString(enabler); 1819 } else { 1820 dest.writeInt(0); 1821 } 1822 dest.writeParcelable(zenPolicy, 0); 1823 dest.writeInt(modified ? 1 : 0); 1824 dest.writeString(pkg); 1825 } 1826 1827 @Override toString()1828 public String toString() { 1829 return new StringBuilder(ZenRule.class.getSimpleName()).append('[') 1830 .append("id=").append(id) 1831 .append(",enabled=").append(String.valueOf(enabled).toUpperCase()) 1832 .append(",snoozing=").append(snoozing) 1833 .append(",name=").append(name) 1834 .append(",zenMode=").append(Global.zenModeToString(zenMode)) 1835 .append(",conditionId=").append(conditionId) 1836 .append(",condition=").append(condition) 1837 .append(",pkg=").append(pkg) 1838 .append(",component=").append(component) 1839 .append(",configActivity=").append(configurationActivity) 1840 .append(",creationTime=").append(creationTime) 1841 .append(",enabler=").append(enabler) 1842 .append(",zenPolicy=").append(zenPolicy) 1843 .append(",modified=").append(modified) 1844 .append(']').toString(); 1845 } 1846 1847 /** @hide */ 1848 // TODO: add configuration activity dumpDebug(ProtoOutputStream proto, long fieldId)1849 public void dumpDebug(ProtoOutputStream proto, long fieldId) { 1850 final long token = proto.start(fieldId); 1851 1852 proto.write(ZenRuleProto.ID, id); 1853 proto.write(ZenRuleProto.NAME, name); 1854 proto.write(ZenRuleProto.CREATION_TIME_MS, creationTime); 1855 proto.write(ZenRuleProto.ENABLED, enabled); 1856 proto.write(ZenRuleProto.ENABLER, enabler); 1857 proto.write(ZenRuleProto.IS_SNOOZING, snoozing); 1858 proto.write(ZenRuleProto.ZEN_MODE, zenMode); 1859 if (conditionId != null) { 1860 proto.write(ZenRuleProto.CONDITION_ID, conditionId.toString()); 1861 } 1862 if (condition != null) { 1863 condition.dumpDebug(proto, ZenRuleProto.CONDITION); 1864 } 1865 if (component != null) { 1866 component.dumpDebug(proto, ZenRuleProto.COMPONENT); 1867 } 1868 if (zenPolicy != null) { 1869 zenPolicy.dumpDebug(proto, ZenRuleProto.ZEN_POLICY); 1870 } 1871 proto.write(ZenRuleProto.MODIFIED, modified); 1872 proto.end(token); 1873 } 1874 appendDiff(Diff d, String item, ZenRule from, ZenRule to)1875 private static void appendDiff(Diff d, String item, ZenRule from, ZenRule to) { 1876 if (d == null) return; 1877 if (from == null) { 1878 if (to != null) { 1879 d.addLine(item, "insert"); 1880 } 1881 return; 1882 } 1883 from.appendDiff(d, item, to); 1884 } 1885 appendDiff(Diff d, String item, ZenRule to)1886 private void appendDiff(Diff d, String item, ZenRule to) { 1887 if (to == null) { 1888 d.addLine(item, "delete"); 1889 return; 1890 } 1891 if (enabled != to.enabled) { 1892 d.addLine(item, "enabled", enabled, to.enabled); 1893 } 1894 if (snoozing != to.snoozing) { 1895 d.addLine(item, "snoozing", snoozing, to.snoozing); 1896 } 1897 if (!Objects.equals(name, to.name)) { 1898 d.addLine(item, "name", name, to.name); 1899 } 1900 if (zenMode != to.zenMode) { 1901 d.addLine(item, "zenMode", zenMode, to.zenMode); 1902 } 1903 if (!Objects.equals(conditionId, to.conditionId)) { 1904 d.addLine(item, "conditionId", conditionId, to.conditionId); 1905 } 1906 if (!Objects.equals(condition, to.condition)) { 1907 d.addLine(item, "condition", condition, to.condition); 1908 } 1909 if (!Objects.equals(component, to.component)) { 1910 d.addLine(item, "component", component, to.component); 1911 } 1912 if (!Objects.equals(configurationActivity, to.configurationActivity)) { 1913 d.addLine(item, "configActivity", configurationActivity, to.configurationActivity); 1914 } 1915 if (!Objects.equals(id, to.id)) { 1916 d.addLine(item, "id", id, to.id); 1917 } 1918 if (creationTime != to.creationTime) { 1919 d.addLine(item, "creationTime", creationTime, to.creationTime); 1920 } 1921 if (!Objects.equals(enabler, to.enabler)) { 1922 d.addLine(item, "enabler", enabler, to.enabler); 1923 } 1924 if (!Objects.equals(zenPolicy, to.zenPolicy)) { 1925 d.addLine(item, "zenPolicy", zenPolicy, to.zenPolicy); 1926 } 1927 if (modified != to.modified) { 1928 d.addLine(item, "modified", modified, to.modified); 1929 } 1930 if (!Objects.equals(pkg, to.pkg)) { 1931 d.addLine(item, "pkg", pkg, to.pkg); 1932 } 1933 } 1934 1935 @Override equals(Object o)1936 public boolean equals(Object o) { 1937 if (!(o instanceof ZenRule)) return false; 1938 if (o == this) return true; 1939 final ZenRule other = (ZenRule) o; 1940 return other.enabled == enabled 1941 && other.snoozing == snoozing 1942 && Objects.equals(other.name, name) 1943 && other.zenMode == zenMode 1944 && Objects.equals(other.conditionId, conditionId) 1945 && Objects.equals(other.condition, condition) 1946 && Objects.equals(other.component, component) 1947 && Objects.equals(other.configurationActivity, configurationActivity) 1948 && Objects.equals(other.id, id) 1949 && Objects.equals(other.enabler, enabler) 1950 && Objects.equals(other.zenPolicy, zenPolicy) 1951 && Objects.equals(other.pkg, pkg) 1952 && other.modified == modified; 1953 } 1954 1955 @Override hashCode()1956 public int hashCode() { 1957 return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition, 1958 component, configurationActivity, pkg, id, enabler, zenPolicy, modified); 1959 } 1960 isAutomaticActive()1961 public boolean isAutomaticActive() { 1962 return enabled && !snoozing && pkg != null && isTrueOrUnknown(); 1963 } 1964 isTrueOrUnknown()1965 public boolean isTrueOrUnknown() { 1966 return condition != null && (condition.state == Condition.STATE_TRUE 1967 || condition.state == Condition.STATE_UNKNOWN); 1968 } 1969 1970 public static final @android.annotation.NonNull Parcelable.Creator<ZenRule> CREATOR 1971 = new Parcelable.Creator<ZenRule>() { 1972 @Override 1973 public ZenRule createFromParcel(Parcel source) { 1974 return new ZenRule(source); 1975 } 1976 @Override 1977 public ZenRule[] newArray(int size) { 1978 return new ZenRule[size]; 1979 } 1980 }; 1981 } 1982 1983 public static class Diff { 1984 private final ArrayList<String> lines = new ArrayList<>(); 1985 1986 @Override toString()1987 public String toString() { 1988 final StringBuilder sb = new StringBuilder("Diff["); 1989 final int N = lines.size(); 1990 for (int i = 0; i < N; i++) { 1991 if (i > 0) { 1992 sb.append(",\n"); 1993 } 1994 sb.append(lines.get(i)); 1995 } 1996 return sb.append(']').toString(); 1997 } 1998 addLine(String item, String action)1999 private Diff addLine(String item, String action) { 2000 lines.add(item + ":" + action); 2001 return this; 2002 } 2003 addLine(String item, String subitem, Object from, Object to)2004 public Diff addLine(String item, String subitem, Object from, Object to) { 2005 return addLine(item + "." + subitem, from, to); 2006 } 2007 addLine(String item, Object from, Object to)2008 public Diff addLine(String item, Object from, Object to) { 2009 return addLine(item, from + "->" + to); 2010 } 2011 } 2012 2013 /** 2014 * Determines whether dnd behavior should mute all ringer-controlled sounds 2015 * This includes notification, ringer and system sounds 2016 */ areAllPriorityOnlyRingerSoundsMuted(NotificationManager.Policy policy)2017 public static boolean areAllPriorityOnlyRingerSoundsMuted(NotificationManager.Policy 2018 policy) { 2019 boolean allowReminders = (policy.priorityCategories 2020 & NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS) != 0; 2021 boolean allowCalls = (policy.priorityCategories 2022 & NotificationManager.Policy.PRIORITY_CATEGORY_CALLS) != 0; 2023 boolean allowMessages = (policy.priorityCategories 2024 & NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) != 0; 2025 boolean allowEvents = (policy.priorityCategories 2026 & NotificationManager.Policy.PRIORITY_CATEGORY_EVENTS) != 0; 2027 boolean allowRepeatCallers = (policy.priorityCategories 2028 & NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS) != 0; 2029 boolean allowConversations = (policy.priorityConversationSenders 2030 & Policy.PRIORITY_CATEGORY_CONVERSATIONS) != 0; 2031 boolean areChannelsBypassingDnd = (policy.state & Policy.STATE_CHANNELS_BYPASSING_DND) != 0; 2032 boolean allowSystem = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_SYSTEM) != 0; 2033 return !allowReminders && !allowCalls && !allowMessages && !allowEvents 2034 && !allowRepeatCallers && !areChannelsBypassingDnd && !allowSystem 2035 && !allowConversations; 2036 } 2037 2038 /** 2039 * Determines whether dnd behavior should mute all sounds 2040 */ areAllZenBehaviorSoundsMuted(NotificationManager.Policy policy)2041 public static boolean areAllZenBehaviorSoundsMuted(NotificationManager.Policy 2042 policy) { 2043 boolean allowAlarms = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS) != 0; 2044 boolean allowMedia = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MEDIA) != 0; 2045 return !allowAlarms && !allowMedia && areAllPriorityOnlyRingerSoundsMuted(policy); 2046 } 2047 2048 /** 2049 * Determines if DND is currently overriding the ringer 2050 */ isZenOverridingRinger(int zen, Policy consolidatedPolicy)2051 public static boolean isZenOverridingRinger(int zen, Policy consolidatedPolicy) { 2052 return zen == Global.ZEN_MODE_NO_INTERRUPTIONS 2053 || zen == Global.ZEN_MODE_ALARMS 2054 || (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS 2055 && ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(consolidatedPolicy)); 2056 } 2057 2058 /** 2059 * Determines whether dnd behavior should mute all ringer-controlled sounds 2060 * This includes notification, ringer and system sounds 2061 */ areAllPriorityOnlyRingerSoundsMuted(ZenModeConfig config)2062 public static boolean areAllPriorityOnlyRingerSoundsMuted(ZenModeConfig config) { 2063 return !config.allowReminders && !config.allowCalls && !config.allowMessages 2064 && !config.allowEvents && !config.allowRepeatCallers 2065 && !config.areChannelsBypassingDnd && !config.allowSystem; 2066 } 2067 2068 /** 2069 * Determines whether dnd mutes all sounds 2070 */ areAllZenBehaviorSoundsMuted(ZenModeConfig config)2071 public static boolean areAllZenBehaviorSoundsMuted(ZenModeConfig config) { 2072 return !config.allowAlarms && !config.allowMedia 2073 && areAllPriorityOnlyRingerSoundsMuted(config); 2074 } 2075 2076 /** 2077 * Returns a description of the current do not disturb settings from config. 2078 * - If turned on manually and end time is known, returns end time. 2079 * - If turned on manually and end time is on forever until turned off, return null if 2080 * describeForeverCondition is false, else return String describing indefinite behavior 2081 * - If turned on by an automatic rule, returns the automatic rule name. 2082 * - If on due to an app, returns the app name. 2083 * - If there's a combination of rules/apps that trigger, then shows the one that will 2084 * last the longest if applicable. 2085 * @return null if DND is off or describeForeverCondition is false and 2086 * DND is on forever (until turned off) 2087 */ getDescription(Context context, boolean zenOn, ZenModeConfig config, boolean describeForeverCondition)2088 public static String getDescription(Context context, boolean zenOn, ZenModeConfig config, 2089 boolean describeForeverCondition) { 2090 if (!zenOn || config == null) { 2091 return null; 2092 } 2093 2094 String secondaryText = ""; 2095 long latestEndTime = -1; 2096 2097 // DND turned on by manual rule 2098 if (config.manualRule != null) { 2099 final Uri id = config.manualRule.conditionId; 2100 if (config.manualRule.enabler != null) { 2101 // app triggered manual rule 2102 String appName = getOwnerCaption(context, config.manualRule.enabler); 2103 if (!appName.isEmpty()) { 2104 secondaryText = appName; 2105 } 2106 } else { 2107 if (id == null) { 2108 // Do not disturb manually triggered to remain on forever until turned off 2109 if (describeForeverCondition) { 2110 return context.getString(R.string.zen_mode_forever); 2111 } else { 2112 return null; 2113 } 2114 } else { 2115 latestEndTime = tryParseCountdownConditionId(id); 2116 if (latestEndTime > 0) { 2117 final CharSequence formattedTime = getFormattedTime(context, 2118 latestEndTime, isToday(latestEndTime), 2119 context.getUserId()); 2120 secondaryText = context.getString(R.string.zen_mode_until, formattedTime); 2121 } 2122 } 2123 } 2124 } 2125 2126 // DND turned on by an automatic rule 2127 for (ZenRule automaticRule : config.automaticRules.values()) { 2128 if (automaticRule.isAutomaticActive()) { 2129 if (isValidEventConditionId(automaticRule.conditionId) 2130 || isValidScheduleConditionId(automaticRule.conditionId)) { 2131 // set text if automatic rule end time is the latest active rule end time 2132 long endTime = parseAutomaticRuleEndTime(context, automaticRule.conditionId); 2133 if (endTime > latestEndTime) { 2134 latestEndTime = endTime; 2135 secondaryText = automaticRule.name; 2136 } 2137 } else { 2138 // set text if 3rd party rule 2139 return automaticRule.name; 2140 } 2141 } 2142 } 2143 2144 return !secondaryText.equals("") ? secondaryText : null; 2145 } 2146 parseAutomaticRuleEndTime(Context context, Uri id)2147 private static long parseAutomaticRuleEndTime(Context context, Uri id) { 2148 if (isValidEventConditionId(id)) { 2149 // cannot look up end times for events 2150 return Long.MAX_VALUE; 2151 } 2152 2153 if (isValidScheduleConditionId(id)) { 2154 ScheduleCalendar schedule = toScheduleCalendar(id); 2155 long endTimeMs = schedule.getNextChangeTime(System.currentTimeMillis()); 2156 2157 // check if automatic rule will end on next alarm 2158 if (schedule.exitAtAlarm()) { 2159 long nextAlarm = getNextAlarm(context); 2160 schedule.maybeSetNextAlarm(System.currentTimeMillis(), nextAlarm); 2161 if (schedule.shouldExitForAlarm(endTimeMs)) { 2162 return nextAlarm; 2163 } 2164 } 2165 2166 return endTimeMs; 2167 } 2168 2169 return -1; 2170 } 2171 getNextAlarm(Context context)2172 private static long getNextAlarm(Context context) { 2173 final AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 2174 final AlarmManager.AlarmClockInfo info = alarms.getNextAlarmClock(context.getUserId()); 2175 return info != null ? info.getTriggerTime() : 0; 2176 } 2177 } 2178