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