1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 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 android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.app.Notification; 22 import android.app.NotificationChannel; 23 import android.os.Parcel; 24 import android.os.Parcelable; 25 import android.util.proto.ProtoOutputStream; 26 27 import java.lang.annotation.Retention; 28 import java.lang.annotation.RetentionPolicy; 29 import java.util.ArrayList; 30 import java.util.Collections; 31 import java.util.Objects; 32 33 /** 34 * ZenPolicy determines whether to allow certain notifications and their corresponding sounds to 35 * play when a device is in Do Not Disturb mode. 36 * ZenPolicy also dictates the visual effects of notifications that are intercepted when 37 * a device is in Do Not Disturb mode. 38 */ 39 public final class ZenPolicy implements Parcelable { 40 private ArrayList<Integer> mPriorityCategories; 41 private ArrayList<Integer> mVisualEffects; 42 private @PeopleType int mPriorityMessages = PEOPLE_TYPE_UNSET; 43 private @PeopleType int mPriorityCalls = PEOPLE_TYPE_UNSET; 44 45 /** @hide */ 46 @IntDef(prefix = { "PRIORITY_CATEGORY_" }, value = { 47 PRIORITY_CATEGORY_REMINDERS, 48 PRIORITY_CATEGORY_EVENTS, 49 PRIORITY_CATEGORY_MESSAGES, 50 PRIORITY_CATEGORY_CALLS, 51 PRIORITY_CATEGORY_REPEAT_CALLERS, 52 PRIORITY_CATEGORY_ALARMS, 53 PRIORITY_CATEGORY_MEDIA, 54 PRIORITY_CATEGORY_SYSTEM, 55 }) 56 @Retention(RetentionPolicy.SOURCE) 57 public @interface PriorityCategory {} 58 59 /** @hide */ 60 public static final int PRIORITY_CATEGORY_REMINDERS = 0; 61 /** @hide */ 62 public static final int PRIORITY_CATEGORY_EVENTS = 1; 63 /** @hide */ 64 public static final int PRIORITY_CATEGORY_MESSAGES = 2; 65 /** @hide */ 66 public static final int PRIORITY_CATEGORY_CALLS = 3; 67 /** @hide */ 68 public static final int PRIORITY_CATEGORY_REPEAT_CALLERS = 4; 69 /** @hide */ 70 public static final int PRIORITY_CATEGORY_ALARMS = 5; 71 /** @hide */ 72 public static final int PRIORITY_CATEGORY_MEDIA = 6; 73 /** @hide */ 74 public static final int PRIORITY_CATEGORY_SYSTEM = 7; 75 76 /** @hide */ 77 @IntDef(prefix = { "VISUAL_EFFECT_" }, value = { 78 VISUAL_EFFECT_FULL_SCREEN_INTENT, 79 VISUAL_EFFECT_LIGHTS, 80 VISUAL_EFFECT_PEEK, 81 VISUAL_EFFECT_STATUS_BAR, 82 VISUAL_EFFECT_BADGE, 83 VISUAL_EFFECT_AMBIENT, 84 VISUAL_EFFECT_NOTIFICATION_LIST, 85 }) 86 @Retention(RetentionPolicy.SOURCE) 87 public @interface VisualEffect {} 88 89 /** @hide */ 90 public static final int VISUAL_EFFECT_FULL_SCREEN_INTENT = 0; 91 /** @hide */ 92 public static final int VISUAL_EFFECT_LIGHTS = 1; 93 /** @hide */ 94 public static final int VISUAL_EFFECT_PEEK = 2; 95 /** @hide */ 96 public static final int VISUAL_EFFECT_STATUS_BAR = 3; 97 /** @hide */ 98 public static final int VISUAL_EFFECT_BADGE = 4; 99 /** @hide */ 100 public static final int VISUAL_EFFECT_AMBIENT = 5; 101 /** @hide */ 102 public static final int VISUAL_EFFECT_NOTIFICATION_LIST = 6; 103 104 /** @hide */ 105 @IntDef(prefix = { "PEOPLE_TYPE_" }, value = { 106 PEOPLE_TYPE_UNSET, 107 PEOPLE_TYPE_ANYONE, 108 PEOPLE_TYPE_CONTACTS, 109 PEOPLE_TYPE_STARRED, 110 PEOPLE_TYPE_NONE, 111 }) 112 @Retention(RetentionPolicy.SOURCE) 113 public @interface PeopleType {} 114 115 /** 116 * Used to indicate no preference for the type of people that can bypass dnd for either 117 * calls or messages. 118 */ 119 public static final int PEOPLE_TYPE_UNSET = 0; 120 121 /** 122 * Used to indicate all calls or messages can bypass dnd. 123 */ 124 public static final int PEOPLE_TYPE_ANYONE = 1; 125 126 /** 127 * Used to indicate calls or messages from contacts can bypass dnd. 128 */ 129 public static final int PEOPLE_TYPE_CONTACTS = 2; 130 131 /** 132 * Used to indicate calls or messages from starred contacts can bypass dnd. 133 */ 134 public static final int PEOPLE_TYPE_STARRED = 3; 135 136 /** 137 * Used to indicate no calls or messages can bypass dnd. 138 */ 139 public static final int PEOPLE_TYPE_NONE = 4; 140 141 /** @hide */ 142 @IntDef(prefix = { "STATE_" }, value = { 143 STATE_UNSET, 144 STATE_ALLOW, 145 STATE_DISALLOW, 146 }) 147 @Retention(RetentionPolicy.SOURCE) 148 public @interface State {} 149 150 /** 151 * Indicates no preference for whether a type of sound or visual effect is or isn't allowed 152 * to play/show when DND is active. Will default to the current set policy. 153 */ 154 public static final int STATE_UNSET = 0; 155 156 /** 157 * Indicates a type of sound or visual effect is allowed to play/show when DND is active. 158 */ 159 public static final int STATE_ALLOW = 1; 160 161 /** 162 * Indicates a type of sound or visual effect is not allowed to play/show when DND is active. 163 */ 164 public static final int STATE_DISALLOW = 2; 165 166 /** @hide */ ZenPolicy()167 public ZenPolicy() { 168 mPriorityCategories = new ArrayList<>(Collections.nCopies(8, 0)); 169 mVisualEffects = new ArrayList<>(Collections.nCopies(7, 0)); 170 } 171 172 /** 173 * Message senders that can bypass DND. 174 * @return {@link #PEOPLE_TYPE_UNSET}, {@link #PEOPLE_TYPE_ANYONE}, 175 * {@link #PEOPLE_TYPE_CONTACTS}, {@link #PEOPLE_TYPE_STARRED} or {@link #PEOPLE_TYPE_NONE} 176 */ getPriorityMessageSenders()177 public @PeopleType int getPriorityMessageSenders() { 178 return mPriorityMessages; 179 } 180 181 /** 182 * Callers that can bypass DND. 183 * @return {@link #PEOPLE_TYPE_UNSET}, {@link #PEOPLE_TYPE_ANYONE}, 184 * {@link #PEOPLE_TYPE_CONTACTS}, {@link #PEOPLE_TYPE_STARRED} or {@link #PEOPLE_TYPE_NONE} 185 */ getPriorityCallSenders()186 public @PeopleType int getPriorityCallSenders() { 187 return mPriorityCalls; 188 } 189 190 /** 191 * Whether this policy wants to allow notifications with category 192 * {@link Notification#CATEGORY_REMINDER} to play sounds and visually appear 193 * or to intercept them when DND is active. 194 * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW} 195 */ getPriorityCategoryReminders()196 public @State int getPriorityCategoryReminders() { 197 return mPriorityCategories.get(PRIORITY_CATEGORY_REMINDERS); 198 } 199 200 /** 201 * Whether this policy wants to allow notifications with category 202 * {@link Notification#CATEGORY_EVENT} to play sounds and visually appear 203 * or to intercept them when DND is active. 204 * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW} 205 */ getPriorityCategoryEvents()206 public @State int getPriorityCategoryEvents() { 207 return mPriorityCategories.get(PRIORITY_CATEGORY_EVENTS); 208 } 209 210 /** 211 * Whether this policy wants to allow notifications with category 212 * {@link Notification#CATEGORY_MESSAGE} to play sounds and visually appear 213 * or to intercept them when DND is active. Types of message senders that are allowed 214 * are specified by {@link #getPriorityMessageSenders}. 215 * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW} 216 */ getPriorityCategoryMessages()217 public @State int getPriorityCategoryMessages() { 218 return mPriorityCategories.get(PRIORITY_CATEGORY_MESSAGES); 219 } 220 221 /** 222 * Whether this policy wants to allow notifications with category 223 * {@link Notification#CATEGORY_CALL} to play sounds and visually appear 224 * or to intercept them when DND is active. Types of callers that are allowed 225 * are specified by {@link #getPriorityCallSenders()}. 226 * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW} 227 */ getPriorityCategoryCalls()228 public @State int getPriorityCategoryCalls() { 229 return mPriorityCategories.get(PRIORITY_CATEGORY_CALLS); 230 } 231 232 /** 233 * Whether this policy wants to allow repeat callers (notifications with category 234 * {@link Notification#CATEGORY_CALL} that have recently called) to play sounds and 235 * visually appear or to intercept them when DND is active. 236 * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW} 237 */ getPriorityCategoryRepeatCallers()238 public @State int getPriorityCategoryRepeatCallers() { 239 return mPriorityCategories.get(PRIORITY_CATEGORY_REPEAT_CALLERS); 240 } 241 242 /** 243 * Whether this policy wants to allow notifications with category 244 * {@link Notification#CATEGORY_ALARM} to play sounds and visually appear 245 * or to intercept them when DND is active. 246 * When alarms are {@link #STATE_DISALLOW disallowed}, the alarm stream will be muted when DND 247 * is active. 248 * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW} 249 */ getPriorityCategoryAlarms()250 public @State int getPriorityCategoryAlarms() { 251 return mPriorityCategories.get(PRIORITY_CATEGORY_ALARMS); 252 } 253 254 /** 255 * Whether this policy wants to allow media notifications to play sounds and visually appear 256 * or to intercept them when DND is active. 257 * When media is {@link #STATE_DISALLOW disallowed}, the media stream will be muted when DND is 258 * active. 259 * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW} 260 */ getPriorityCategoryMedia()261 public @State int getPriorityCategoryMedia() { 262 return mPriorityCategories.get(PRIORITY_CATEGORY_MEDIA); 263 } 264 265 /** 266 * Whether this policy wants to allow system sounds when DND is active. 267 * When system is {@link #STATE_DISALLOW}, the system stream will be muted when DND is active. 268 * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW} 269 */ getPriorityCategorySystem()270 public @State int getPriorityCategorySystem() { 271 return mPriorityCategories.get(PRIORITY_CATEGORY_SYSTEM); 272 } 273 274 /** 275 * Whether this policy allows {@link Notification#fullScreenIntent full screen intents} from 276 * notifications intercepted by DND. 277 */ getVisualEffectFullScreenIntent()278 public @State int getVisualEffectFullScreenIntent() { 279 return mVisualEffects.get(VISUAL_EFFECT_FULL_SCREEN_INTENT); 280 } 281 282 /** 283 * Whether this policy allows {@link NotificationChannel#shouldShowLights() notification 284 * lights} from notifications intercepted by DND. 285 */ getVisualEffectLights()286 public @State int getVisualEffectLights() { 287 return mVisualEffects.get(VISUAL_EFFECT_LIGHTS); 288 } 289 290 /** 291 * Whether this policy allows peeking from notifications intercepted by DND. 292 */ getVisualEffectPeek()293 public @State int getVisualEffectPeek() { 294 return mVisualEffects.get(VISUAL_EFFECT_PEEK); 295 } 296 297 /** 298 * Whether this policy allows notifications intercepted by DND from appearing in the status bar 299 * on devices that support status bars. 300 */ getVisualEffectStatusBar()301 public @State int getVisualEffectStatusBar() { 302 return mVisualEffects.get(VISUAL_EFFECT_STATUS_BAR); 303 } 304 305 /** 306 * Whether this policy allows {@link NotificationChannel#canShowBadge() badges} from 307 * notifications intercepted by DND on devices that support badging. 308 */ getVisualEffectBadge()309 public @State int getVisualEffectBadge() { 310 return mVisualEffects.get(VISUAL_EFFECT_BADGE); 311 } 312 313 /** 314 * Whether this policy allows notifications intercepted by DND from appearing on ambient 315 * displays on devices that support ambient display. 316 */ getVisualEffectAmbient()317 public @State int getVisualEffectAmbient() { 318 return mVisualEffects.get(VISUAL_EFFECT_AMBIENT); 319 } 320 321 /** 322 * Whether this policy allows notifications intercepted by DND from appearing in notification 323 * list views like the notification shade or lockscreen on devices that support those 324 * views. 325 */ getVisualEffectNotificationList()326 public @State int getVisualEffectNotificationList() { 327 return mVisualEffects.get(VISUAL_EFFECT_NOTIFICATION_LIST); 328 } 329 330 /** 331 * Whether this policy hides all visual effects 332 * @hide 333 */ shouldHideAllVisualEffects()334 public boolean shouldHideAllVisualEffects() { 335 for (int i = 0; i < mVisualEffects.size(); i++) { 336 if (mVisualEffects.get(i) != STATE_DISALLOW) { 337 return false; 338 } 339 } 340 return true; 341 } 342 343 /** 344 * Whether this policy shows all visual effects 345 * @hide 346 */ shouldShowAllVisualEffects()347 public boolean shouldShowAllVisualEffects() { 348 for (int i = 0; i < mVisualEffects.size(); i++) { 349 if (mVisualEffects.get(i) != STATE_ALLOW) { 350 return false; 351 } 352 } 353 return true; 354 } 355 356 /** 357 * Builder class for {@link ZenPolicy} objects. 358 * Provides a convenient way to set the various fields of a {@link ZenPolicy}. If a field 359 * is not set, it is (@link STATE_UNSET} and will not change the current set policy. 360 */ 361 public static final class Builder { 362 private ZenPolicy mZenPolicy; 363 Builder()364 public Builder() { 365 mZenPolicy = new ZenPolicy(); 366 } 367 368 /** 369 * @hide 370 */ Builder(ZenPolicy policy)371 public Builder(ZenPolicy policy) { 372 if (policy != null) { 373 mZenPolicy = policy.copy(); 374 } else { 375 mZenPolicy = new ZenPolicy(); 376 } 377 } 378 379 /** 380 * Builds the current ZenPolicy. 381 */ build()382 public @NonNull ZenPolicy build() { 383 return mZenPolicy.copy(); 384 } 385 386 /** 387 * Allows all notifications to bypass DND and unmutes all streams. 388 */ allowAllSounds()389 public @NonNull Builder allowAllSounds() { 390 for (int i = 0; i < mZenPolicy.mPriorityCategories.size(); i++) { 391 mZenPolicy.mPriorityCategories.set(i, STATE_ALLOW); 392 } 393 mZenPolicy.mPriorityMessages = PEOPLE_TYPE_ANYONE; 394 mZenPolicy.mPriorityCalls = PEOPLE_TYPE_ANYONE; 395 return this; 396 } 397 398 /** 399 * Intercepts all notifications and prevents them from playing sounds 400 * when DND is active. Also mutes alarm, system and media streams. 401 * Notification channels can still play sounds only if they 402 * {@link NotificationChannel#canBypassDnd can bypass DND}. If no channels can bypass DND, 403 * the ringer stream is also muted. 404 */ disallowAllSounds()405 public @NonNull Builder disallowAllSounds() { 406 for (int i = 0; i < mZenPolicy.mPriorityCategories.size(); i++) { 407 mZenPolicy.mPriorityCategories.set(i, STATE_DISALLOW); 408 } 409 mZenPolicy.mPriorityMessages = PEOPLE_TYPE_NONE; 410 mZenPolicy.mPriorityCalls = PEOPLE_TYPE_NONE; 411 return this; 412 } 413 414 /** 415 * Allows notifications intercepted by DND to show on all surfaces when DND is active. 416 */ showAllVisualEffects()417 public @NonNull Builder showAllVisualEffects() { 418 for (int i = 0; i < mZenPolicy.mVisualEffects.size(); i++) { 419 mZenPolicy.mVisualEffects.set(i, STATE_ALLOW); 420 } 421 return this; 422 } 423 424 /** 425 * Disallows notifications intercepted by DND from showing when DND is active. 426 */ hideAllVisualEffects()427 public @NonNull Builder hideAllVisualEffects() { 428 for (int i = 0; i < mZenPolicy.mVisualEffects.size(); i++) { 429 mZenPolicy.mVisualEffects.set(i, STATE_DISALLOW); 430 } 431 return this; 432 } 433 434 /** 435 * Unsets a priority category, neither allowing or disallowing. When applying this policy, 436 * unset categories will default to the current applied policy. 437 * @hide 438 */ unsetPriorityCategory(@riorityCategory int category)439 public @NonNull Builder unsetPriorityCategory(@PriorityCategory int category) { 440 mZenPolicy.mPriorityCategories.set(category, STATE_UNSET); 441 442 if (category == PRIORITY_CATEGORY_MESSAGES) { 443 mZenPolicy.mPriorityMessages = STATE_UNSET; 444 } else if (category == PRIORITY_CATEGORY_CALLS) { 445 mZenPolicy.mPriorityCalls = STATE_UNSET; 446 } 447 448 return this; 449 } 450 451 /** 452 * Unsets a visual effect, neither allowing or disallowing. When applying this policy, 453 * unset effects will default to the current applied policy. 454 * @hide 455 */ unsetVisualEffect(@isualEffect int effect)456 public @NonNull Builder unsetVisualEffect(@VisualEffect int effect) { 457 mZenPolicy.mVisualEffects.set(effect, STATE_UNSET); 458 return this; 459 } 460 461 /** 462 * Whether to allow notifications with category {@link Notification#CATEGORY_REMINDER} 463 * to play sounds and visually appear or to intercept them when DND is active. 464 */ allowReminders(boolean allow)465 public @NonNull Builder allowReminders(boolean allow) { 466 mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_REMINDERS, 467 allow ? STATE_ALLOW : STATE_DISALLOW); 468 return this; 469 } 470 471 /** 472 * Whether to allow notifications with category {@link Notification#CATEGORY_EVENT} 473 * to play sounds and visually appear or to intercept them when DND is active. 474 */ allowEvents(boolean allow)475 public @NonNull Builder allowEvents(boolean allow) { 476 mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_EVENTS, 477 allow ? STATE_ALLOW : STATE_DISALLOW); 478 return this; 479 } 480 481 /** 482 * Whether to allow notifications with category {@link Notification#CATEGORY_MESSAGE} 483 * that match audienceType to play sounds and visually appear or to intercept 484 * them when DND is active. 485 * @param audienceType message senders that are allowed to bypass DND 486 */ allowMessages(@eopleType int audienceType)487 public @NonNull Builder allowMessages(@PeopleType int audienceType) { 488 if (audienceType == STATE_UNSET) { 489 return unsetPriorityCategory(PRIORITY_CATEGORY_MESSAGES); 490 } 491 492 if (audienceType == PEOPLE_TYPE_NONE) { 493 mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_MESSAGES, STATE_DISALLOW); 494 } else if (audienceType == PEOPLE_TYPE_ANYONE || audienceType == PEOPLE_TYPE_CONTACTS 495 || audienceType == PEOPLE_TYPE_STARRED) { 496 mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_MESSAGES, STATE_ALLOW); 497 } else { 498 return this; 499 } 500 501 mZenPolicy.mPriorityMessages = audienceType; 502 return this; 503 } 504 505 /** 506 * Whether to allow notifications with category {@link Notification#CATEGORY_CALL} 507 * that match audienceType to play sounds and visually appear or to intercept 508 * them when DND is active. 509 * @param audienceType callers that are allowed to bypass DND 510 */ allowCalls(@eopleType int audienceType)511 public @NonNull Builder allowCalls(@PeopleType int audienceType) { 512 if (audienceType == STATE_UNSET) { 513 return unsetPriorityCategory(PRIORITY_CATEGORY_CALLS); 514 } 515 516 if (audienceType == PEOPLE_TYPE_NONE) { 517 mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_CALLS, STATE_DISALLOW); 518 } else if (audienceType == PEOPLE_TYPE_ANYONE || audienceType == PEOPLE_TYPE_CONTACTS 519 || audienceType == PEOPLE_TYPE_STARRED) { 520 mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_CALLS, STATE_ALLOW); 521 } else { 522 return this; 523 } 524 525 mZenPolicy.mPriorityCalls = audienceType; 526 return this; 527 } 528 529 /** 530 * Whether to allow repeat callers (notifications with category 531 * {@link Notification#CATEGORY_CALL} that have recently called 532 * to play sounds and visually appear. 533 */ allowRepeatCallers(boolean allow)534 public @NonNull Builder allowRepeatCallers(boolean allow) { 535 mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_REPEAT_CALLERS, 536 allow ? STATE_ALLOW : STATE_DISALLOW); 537 return this; 538 } 539 540 541 /** 542 * Whether to allow notifications with category {@link Notification#CATEGORY_ALARM} 543 * to play sounds and visually appear or to intercept them when DND is active. 544 * Disallowing alarms will mute the alarm stream when DND is active. 545 */ allowAlarms(boolean allow)546 public @NonNull Builder allowAlarms(boolean allow) { 547 mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_ALARMS, 548 allow ? STATE_ALLOW : STATE_DISALLOW); 549 return this; 550 } 551 552 /** 553 * Whether to allow media notifications to play sounds and visually 554 * appear or to intercept them when DND is active. 555 * Disallowing media will mute the media stream when DND is active. 556 */ allowMedia(boolean allow)557 public @NonNull Builder allowMedia(boolean allow) { 558 mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_MEDIA, 559 allow ? STATE_ALLOW : STATE_DISALLOW); 560 return this; 561 } 562 563 /** 564 * Whether to allow system sounds to play when DND is active. 565 * Disallowing system sounds will mute the system stream when DND is active. 566 */ allowSystem(boolean allow)567 public @NonNull Builder allowSystem(boolean allow) { 568 mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_SYSTEM, 569 allow ? STATE_ALLOW : STATE_DISALLOW); 570 return this; 571 } 572 573 /** 574 * Whether to allow {@link PriorityCategory} sounds to play when DND is active. 575 * @hide 576 */ allowCategory(@riorityCategory int category, boolean allow)577 public @NonNull Builder allowCategory(@PriorityCategory int category, boolean allow) { 578 switch (category) { 579 case PRIORITY_CATEGORY_ALARMS: 580 allowAlarms(allow); 581 break; 582 case PRIORITY_CATEGORY_MEDIA: 583 allowMedia(allow); 584 break; 585 case PRIORITY_CATEGORY_SYSTEM: 586 allowSystem(allow); 587 break; 588 case PRIORITY_CATEGORY_REMINDERS: 589 allowReminders(allow); 590 break; 591 case PRIORITY_CATEGORY_EVENTS: 592 allowEvents(allow); 593 break; 594 case PRIORITY_CATEGORY_REPEAT_CALLERS: 595 allowRepeatCallers(allow); 596 break; 597 } 598 return this; 599 } 600 601 /** 602 * Whether {@link Notification#fullScreenIntent full screen intents} that are intercepted 603 * by DND are shown. 604 */ showFullScreenIntent(boolean show)605 public @NonNull Builder showFullScreenIntent(boolean show) { 606 mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_FULL_SCREEN_INTENT, 607 show ? STATE_ALLOW : STATE_DISALLOW); 608 return this; 609 } 610 611 /** 612 * Whether {@link NotificationChannel#shouldShowLights() notification lights} from 613 * notifications intercepted by DND are blocked. 614 */ showLights(boolean show)615 public @NonNull Builder showLights(boolean show) { 616 mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_LIGHTS, 617 show ? STATE_ALLOW : STATE_DISALLOW); 618 return this; 619 } 620 621 /** 622 * Whether notifications intercepted by DND are prevented from peeking. 623 */ showPeeking(boolean show)624 public @NonNull Builder showPeeking(boolean show) { 625 mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_PEEK, 626 show ? STATE_ALLOW : STATE_DISALLOW); 627 return this; 628 } 629 630 /** 631 * Whether notifications intercepted by DND are prevented from appearing in the status bar 632 * on devices that support status bars. 633 */ showStatusBarIcons(boolean show)634 public @NonNull Builder showStatusBarIcons(boolean show) { 635 mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_STATUS_BAR, 636 show ? STATE_ALLOW : STATE_DISALLOW); 637 return this; 638 } 639 640 /** 641 * Whether {@link NotificationChannel#canShowBadge() badges} from 642 * notifications intercepted by DND are allowed on devices that support badging. 643 */ showBadges(boolean show)644 public @NonNull Builder showBadges(boolean show) { 645 mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_BADGE, 646 show ? STATE_ALLOW : STATE_DISALLOW); 647 return this; 648 } 649 650 /** 651 * Whether notification intercepted by DND are prevented from appearing on ambient displays 652 * on devices that support ambient display. 653 */ showInAmbientDisplay(boolean show)654 public @NonNull Builder showInAmbientDisplay(boolean show) { 655 mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_AMBIENT, 656 show ? STATE_ALLOW : STATE_DISALLOW); 657 return this; 658 } 659 660 /** 661 * Whether notification intercepted by DND are prevented from appearing in notification 662 * list views like the notification shade or lockscreen on devices that support those 663 * views. 664 */ showInNotificationList(boolean show)665 public @NonNull Builder showInNotificationList(boolean show) { 666 mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_NOTIFICATION_LIST, 667 show ? STATE_ALLOW : STATE_DISALLOW); 668 return this; 669 } 670 671 /** 672 * Whether notifications intercepted by DND are prevented from appearing for 673 * {@link VisualEffect} 674 * @hide 675 */ showVisualEffect(@isualEffect int effect, boolean show)676 public @NonNull Builder showVisualEffect(@VisualEffect int effect, boolean show) { 677 switch (effect) { 678 case VISUAL_EFFECT_FULL_SCREEN_INTENT: 679 showFullScreenIntent(show); 680 break; 681 case VISUAL_EFFECT_LIGHTS: 682 showLights(show); 683 break; 684 case VISUAL_EFFECT_PEEK: 685 showPeeking(show); 686 break; 687 case VISUAL_EFFECT_STATUS_BAR: 688 showStatusBarIcons(show); 689 break; 690 case VISUAL_EFFECT_BADGE: 691 showBadges(show); 692 break; 693 case VISUAL_EFFECT_AMBIENT: 694 showInAmbientDisplay(show); 695 break; 696 case VISUAL_EFFECT_NOTIFICATION_LIST: 697 showInNotificationList(show); 698 break; 699 } 700 return this; 701 } 702 } 703 704 @Override describeContents()705 public int describeContents() { 706 return 0; 707 } 708 709 @Override writeToParcel(Parcel dest, int flags)710 public void writeToParcel(Parcel dest, int flags) { 711 dest.writeList(mPriorityCategories); 712 dest.writeList(mVisualEffects); 713 dest.writeInt(mPriorityCalls); 714 dest.writeInt(mPriorityMessages); 715 } 716 717 public static final @android.annotation.NonNull Parcelable.Creator<ZenPolicy> CREATOR = 718 new Parcelable.Creator<ZenPolicy>() { 719 @Override 720 public ZenPolicy createFromParcel(Parcel source) { 721 ZenPolicy policy = new ZenPolicy(); 722 policy.mPriorityCategories = source.readArrayList(Integer.class.getClassLoader()); 723 policy.mVisualEffects = source.readArrayList(Integer.class.getClassLoader()); 724 policy.mPriorityCalls = source.readInt(); 725 policy.mPriorityMessages = source.readInt(); 726 return policy; 727 } 728 729 @Override 730 public ZenPolicy[] newArray(int size) { 731 return new ZenPolicy[size]; 732 } 733 }; 734 735 @Override toString()736 public String toString() { 737 return new StringBuilder(ZenPolicy.class.getSimpleName()) 738 .append('{') 739 .append("priorityCategories=[").append(priorityCategoriesToString()) 740 .append("], visualEffects=[").append(visualEffectsToString()) 741 .append("], priorityCalls=").append(peopleTypeToString(mPriorityCalls)) 742 .append(", priorityMessages=").append(peopleTypeToString(mPriorityMessages)) 743 .append('}') 744 .toString(); 745 } 746 747 priorityCategoriesToString()748 private String priorityCategoriesToString() { 749 StringBuilder builder = new StringBuilder(); 750 for (int i = 0; i < mPriorityCategories.size(); i++) { 751 if (mPriorityCategories.get(i) != STATE_UNSET) { 752 builder.append(indexToCategory(i)) 753 .append("=") 754 .append(stateToString(mPriorityCategories.get(i))) 755 .append(" "); 756 } 757 758 } 759 return builder.toString(); 760 } 761 visualEffectsToString()762 private String visualEffectsToString() { 763 StringBuilder builder = new StringBuilder(); 764 for (int i = 0; i < mVisualEffects.size(); i++) { 765 if (mVisualEffects.get(i) != STATE_UNSET) { 766 builder.append(indexToVisualEffect(i)) 767 .append("=") 768 .append(stateToString(mVisualEffects.get(i))) 769 .append(" "); 770 } 771 772 } 773 return builder.toString(); 774 } 775 indexToVisualEffect(@isualEffect int visualEffectIndex)776 private String indexToVisualEffect(@VisualEffect int visualEffectIndex) { 777 switch (visualEffectIndex) { 778 case VISUAL_EFFECT_FULL_SCREEN_INTENT: 779 return "fullScreenIntent"; 780 case VISUAL_EFFECT_LIGHTS: 781 return "lights"; 782 case VISUAL_EFFECT_PEEK: 783 return "peek"; 784 case VISUAL_EFFECT_STATUS_BAR: 785 return "statusBar"; 786 case VISUAL_EFFECT_BADGE: 787 return "badge"; 788 case VISUAL_EFFECT_AMBIENT: 789 return "ambient"; 790 case VISUAL_EFFECT_NOTIFICATION_LIST: 791 return "notificationList"; 792 } 793 return null; 794 } 795 indexToCategory(@riorityCategory int categoryIndex)796 private String indexToCategory(@PriorityCategory int categoryIndex) { 797 switch (categoryIndex) { 798 case PRIORITY_CATEGORY_REMINDERS: 799 return "reminders"; 800 case PRIORITY_CATEGORY_EVENTS: 801 return "events"; 802 case PRIORITY_CATEGORY_MESSAGES: 803 return "messages"; 804 case PRIORITY_CATEGORY_CALLS: 805 return "calls"; 806 case PRIORITY_CATEGORY_REPEAT_CALLERS: 807 return "repeatCallers"; 808 case PRIORITY_CATEGORY_ALARMS: 809 return "alarms"; 810 case PRIORITY_CATEGORY_MEDIA: 811 return "media"; 812 case PRIORITY_CATEGORY_SYSTEM: 813 return "system"; 814 } 815 return null; 816 } 817 stateToString(@tate int state)818 private String stateToString(@State int state) { 819 switch (state) { 820 case STATE_UNSET: 821 return "unset"; 822 case STATE_DISALLOW: 823 return "disallow"; 824 case STATE_ALLOW: 825 return "allow"; 826 } 827 return "invalidState{" + state + "}"; 828 } 829 peopleTypeToString(@eopleType int peopleType)830 private String peopleTypeToString(@PeopleType int peopleType) { 831 switch (peopleType) { 832 case PEOPLE_TYPE_ANYONE: 833 return "anyone"; 834 case PEOPLE_TYPE_CONTACTS: 835 return "contacts"; 836 case PEOPLE_TYPE_NONE: 837 return "none"; 838 case PEOPLE_TYPE_STARRED: 839 return "starred_contacts"; 840 case STATE_UNSET: 841 return "unset"; 842 } 843 return "invalidPeopleType{" + peopleType + "}"; 844 } 845 846 @Override equals(Object o)847 public boolean equals(Object o) { 848 if (!(o instanceof ZenPolicy)) return false; 849 if (o == this) return true; 850 final ZenPolicy other = (ZenPolicy) o; 851 852 return Objects.equals(other.mPriorityCategories, mPriorityCategories) 853 && Objects.equals(other.mVisualEffects, mVisualEffects) 854 && other.mPriorityCalls == mPriorityCalls 855 && other.mPriorityMessages == mPriorityMessages; 856 } 857 858 @Override hashCode()859 public int hashCode() { 860 return Objects.hash(mPriorityCategories, mVisualEffects, mPriorityCalls, mPriorityMessages); 861 } 862 getZenPolicyPriorityCategoryState(@riorityCategory int category)863 private @ZenPolicy.State int getZenPolicyPriorityCategoryState(@PriorityCategory int 864 category) { 865 switch (category) { 866 case PRIORITY_CATEGORY_REMINDERS: 867 return getPriorityCategoryReminders(); 868 case PRIORITY_CATEGORY_EVENTS: 869 return getPriorityCategoryEvents(); 870 case PRIORITY_CATEGORY_MESSAGES: 871 return getPriorityCategoryMessages(); 872 case PRIORITY_CATEGORY_CALLS: 873 return getPriorityCategoryCalls(); 874 case PRIORITY_CATEGORY_REPEAT_CALLERS: 875 return getPriorityCategoryRepeatCallers(); 876 case PRIORITY_CATEGORY_ALARMS: 877 return getPriorityCategoryAlarms(); 878 case PRIORITY_CATEGORY_MEDIA: 879 return getPriorityCategoryMedia(); 880 case PRIORITY_CATEGORY_SYSTEM: 881 return getPriorityCategorySystem(); 882 } 883 return -1; 884 } 885 getZenPolicyVisualEffectState(@isualEffect int effect)886 private @ZenPolicy.State int getZenPolicyVisualEffectState(@VisualEffect int effect) { 887 switch (effect) { 888 case VISUAL_EFFECT_FULL_SCREEN_INTENT: 889 return getVisualEffectFullScreenIntent(); 890 case VISUAL_EFFECT_LIGHTS: 891 return getVisualEffectLights(); 892 case VISUAL_EFFECT_PEEK: 893 return getVisualEffectPeek(); 894 case VISUAL_EFFECT_STATUS_BAR: 895 return getVisualEffectStatusBar(); 896 case VISUAL_EFFECT_BADGE: 897 return getVisualEffectBadge(); 898 case VISUAL_EFFECT_AMBIENT: 899 return getVisualEffectAmbient(); 900 case VISUAL_EFFECT_NOTIFICATION_LIST: 901 return getVisualEffectNotificationList(); 902 } 903 return -1; 904 } 905 906 /** @hide */ isCategoryAllowed(@riorityCategory int category, boolean defaultVal)907 public boolean isCategoryAllowed(@PriorityCategory int category, boolean defaultVal) { 908 switch (getZenPolicyPriorityCategoryState(category)) { 909 case ZenPolicy.STATE_ALLOW: 910 return true; 911 case ZenPolicy.STATE_DISALLOW: 912 return false; 913 default: 914 return defaultVal; 915 } 916 } 917 918 /** @hide */ isVisualEffectAllowed(@isualEffect int effect, boolean defaultVal)919 public boolean isVisualEffectAllowed(@VisualEffect int effect, boolean defaultVal) { 920 switch (getZenPolicyVisualEffectState(effect)) { 921 case ZenPolicy.STATE_ALLOW: 922 return true; 923 case ZenPolicy.STATE_DISALLOW: 924 return false; 925 default: 926 return defaultVal; 927 } 928 } 929 930 /** 931 * Applies another policy on top of this policy 932 * @hide 933 */ apply(ZenPolicy policyToApply)934 public void apply(ZenPolicy policyToApply) { 935 if (policyToApply == null) { 936 return; 937 } 938 939 // apply priority categories 940 for (int category = 0; category < mPriorityCategories.size(); category++) { 941 if (mPriorityCategories.get(category) == STATE_DISALLOW) { 942 // if a priority category is already disallowed by the policy, cannot allow 943 continue; 944 } 945 946 @State int newState = policyToApply.mPriorityCategories.get(category); 947 if (newState != STATE_UNSET) { 948 mPriorityCategories.set(category, newState); 949 950 if (category == PRIORITY_CATEGORY_MESSAGES 951 && mPriorityMessages < policyToApply.mPriorityMessages) { 952 mPriorityMessages = policyToApply.mPriorityMessages; 953 } else if (category == PRIORITY_CATEGORY_CALLS 954 && mPriorityCalls < policyToApply.mPriorityCalls) { 955 mPriorityCalls = policyToApply.mPriorityCalls; 956 } 957 } 958 } 959 960 // apply visual effects 961 for (int visualEffect = 0; visualEffect < mVisualEffects.size(); visualEffect++) { 962 if (mVisualEffects.get(visualEffect) == STATE_DISALLOW) { 963 // if a visual effect is already disallowed by the policy, cannot allow 964 continue; 965 } 966 967 if (policyToApply.mVisualEffects.get(visualEffect) != STATE_UNSET) { 968 mVisualEffects.set(visualEffect, policyToApply.mVisualEffects.get(visualEffect)); 969 } 970 } 971 } 972 973 /** 974 * @hide 975 */ writeToProto(ProtoOutputStream proto, long fieldId)976 public void writeToProto(ProtoOutputStream proto, long fieldId) { 977 final long token = proto.start(fieldId); 978 979 proto.write(ZenPolicyProto.REMINDERS, getPriorityCategoryReminders()); 980 proto.write(ZenPolicyProto.EVENTS, getPriorityCategoryEvents()); 981 proto.write(ZenPolicyProto.MESSAGES, getPriorityCategoryMessages()); 982 proto.write(ZenPolicyProto.CALLS, getPriorityCategoryCalls()); 983 proto.write(ZenPolicyProto.REPEAT_CALLERS, getPriorityCategoryRepeatCallers()); 984 proto.write(ZenPolicyProto.ALARMS, getPriorityCategoryAlarms()); 985 proto.write(ZenPolicyProto.MEDIA, getPriorityCategoryMedia()); 986 proto.write(ZenPolicyProto.SYSTEM, getPriorityCategorySystem()); 987 988 proto.write(ZenPolicyProto.FULL_SCREEN_INTENT, getVisualEffectFullScreenIntent()); 989 proto.write(ZenPolicyProto.LIGHTS, getVisualEffectLights()); 990 proto.write(ZenPolicyProto.PEEK, getVisualEffectPeek()); 991 proto.write(ZenPolicyProto.STATUS_BAR, getVisualEffectStatusBar()); 992 proto.write(ZenPolicyProto.BADGE, getVisualEffectBadge()); 993 proto.write(ZenPolicyProto.AMBIENT, getVisualEffectAmbient()); 994 proto.write(ZenPolicyProto.NOTIFICATION_LIST, getVisualEffectNotificationList()); 995 996 proto.write(ZenPolicyProto.PRIORITY_MESSAGES, getPriorityMessageSenders()); 997 proto.write(ZenPolicyProto.PRIORITY_CALLS, getPriorityCallSenders()); 998 proto.end(token); 999 } 1000 1001 /** 1002 * Makes deep copy of this ZenPolicy. 1003 * @hide 1004 */ copy()1005 public @NonNull ZenPolicy copy() { 1006 final Parcel parcel = Parcel.obtain(); 1007 try { 1008 writeToParcel(parcel, 0); 1009 parcel.setDataPosition(0); 1010 return CREATOR.createFromParcel(parcel); 1011 } finally { 1012 parcel.recycle(); 1013 } 1014 } 1015 } 1016