1574 lines
58 KiB
Java
1574 lines
58 KiB
Java
/*
|
|
* Copyright (C) 2018 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package android.service.notification;
|
|
|
|
import android.annotation.FlaggedApi;
|
|
import android.annotation.IntDef;
|
|
import android.annotation.NonNull;
|
|
import android.annotation.Nullable;
|
|
import android.annotation.SuppressLint;
|
|
import android.annotation.TestApi;
|
|
import android.app.Flags;
|
|
import android.app.Notification;
|
|
import android.app.NotificationChannel;
|
|
import android.os.Parcel;
|
|
import android.os.Parcelable;
|
|
import android.util.proto.ProtoOutputStream;
|
|
|
|
import java.io.ByteArrayOutputStream;
|
|
import java.lang.annotation.Retention;
|
|
import java.lang.annotation.RetentionPolicy;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
import java.util.Objects;
|
|
|
|
/**
|
|
* ZenPolicy determines whether to allow certain notifications and their corresponding sounds to
|
|
* play when a device is in Do Not Disturb mode.
|
|
* ZenPolicy also dictates the visual effects of notifications that are intercepted when
|
|
* a device is in Do Not Disturb mode.
|
|
*/
|
|
public final class ZenPolicy implements Parcelable {
|
|
|
|
/**
|
|
* Enum for the user-modifiable fields in this object.
|
|
* @hide
|
|
*/
|
|
@IntDef(flag = true, prefix = { "FIELD_" }, value = {
|
|
FIELD_MESSAGES,
|
|
FIELD_CALLS,
|
|
FIELD_CONVERSATIONS,
|
|
FIELD_ALLOW_CHANNELS,
|
|
FIELD_PRIORITY_CATEGORY_REMINDERS,
|
|
FIELD_PRIORITY_CATEGORY_EVENTS,
|
|
FIELD_PRIORITY_CATEGORY_REPEAT_CALLERS,
|
|
FIELD_PRIORITY_CATEGORY_ALARMS,
|
|
FIELD_PRIORITY_CATEGORY_MEDIA,
|
|
FIELD_PRIORITY_CATEGORY_SYSTEM,
|
|
FIELD_VISUAL_EFFECT_FULL_SCREEN_INTENT,
|
|
FIELD_VISUAL_EFFECT_LIGHTS,
|
|
FIELD_VISUAL_EFFECT_PEEK,
|
|
FIELD_VISUAL_EFFECT_STATUS_BAR,
|
|
FIELD_VISUAL_EFFECT_BADGE,
|
|
FIELD_VISUAL_EFFECT_AMBIENT,
|
|
FIELD_VISUAL_EFFECT_NOTIFICATION_LIST,
|
|
})
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
public @interface ModifiableField {}
|
|
|
|
/**
|
|
* Covers modifications to MESSAGE_SENDERS and PRIORITY_CATEGORY_MESSAGES, which are set at
|
|
* the same time.
|
|
* @hide
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_MODES_API)
|
|
public static final int FIELD_MESSAGES = 1 << 0;
|
|
/**
|
|
* Covers modifications to CALL_SENDERS and PRIORITY_CATEGORY_CALLS, which are set at
|
|
* the same time.
|
|
* @hide
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_MODES_API)
|
|
public static final int FIELD_CALLS = 1 << 1;
|
|
/**
|
|
* Covers modifications to CONVERSATION_SENDERS and PRIORITY_CATEGORY_CONVERSATIONS, which are
|
|
* set at the same time.
|
|
* @hide
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_MODES_API)
|
|
public static final int FIELD_CONVERSATIONS = 1 << 2;
|
|
/**
|
|
* @hide
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_MODES_API)
|
|
public static final int FIELD_ALLOW_CHANNELS = 1 << 3;
|
|
/**
|
|
* @hide
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_MODES_API)
|
|
public static final int FIELD_PRIORITY_CATEGORY_REMINDERS = 1 << 4;
|
|
/**
|
|
* @hide
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_MODES_API)
|
|
public static final int FIELD_PRIORITY_CATEGORY_EVENTS = 1 << 5;
|
|
/**
|
|
* @hide
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_MODES_API)
|
|
public static final int FIELD_PRIORITY_CATEGORY_REPEAT_CALLERS = 1 << 6;
|
|
/**
|
|
* @hide
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_MODES_API)
|
|
public static final int FIELD_PRIORITY_CATEGORY_ALARMS = 1 << 7;
|
|
/**
|
|
* @hide
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_MODES_API)
|
|
public static final int FIELD_PRIORITY_CATEGORY_MEDIA = 1 << 8;
|
|
/**
|
|
* @hide
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_MODES_API)
|
|
public static final int FIELD_PRIORITY_CATEGORY_SYSTEM = 1 << 9;
|
|
/**
|
|
* @hide
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_MODES_API)
|
|
public static final int FIELD_VISUAL_EFFECT_FULL_SCREEN_INTENT = 1 << 10;
|
|
/**
|
|
* @hide
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_MODES_API)
|
|
public static final int FIELD_VISUAL_EFFECT_LIGHTS = 1 << 11;
|
|
/**
|
|
* @hide
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_MODES_API)
|
|
public static final int FIELD_VISUAL_EFFECT_PEEK = 1 << 12;
|
|
/**
|
|
* @hide
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_MODES_API)
|
|
public static final int FIELD_VISUAL_EFFECT_STATUS_BAR = 1 << 13;
|
|
/**
|
|
* @hide
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_MODES_API)
|
|
public static final int FIELD_VISUAL_EFFECT_BADGE = 1 << 14;
|
|
/**
|
|
* @hide
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_MODES_API)
|
|
public static final int FIELD_VISUAL_EFFECT_AMBIENT = 1 << 15;
|
|
/**
|
|
* @hide
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_MODES_API)
|
|
public static final int FIELD_VISUAL_EFFECT_NOTIFICATION_LIST = 1 << 16;
|
|
|
|
private List<Integer> mPriorityCategories;
|
|
private List<Integer> mVisualEffects;
|
|
private @PeopleType int mPriorityMessages = PEOPLE_TYPE_UNSET;
|
|
private @PeopleType int mPriorityCalls = PEOPLE_TYPE_UNSET;
|
|
private @ConversationSenders int mConversationSenders = CONVERSATION_SENDERS_UNSET;
|
|
@FlaggedApi(Flags.FLAG_MODES_API)
|
|
private @ChannelType int mAllowChannels = CHANNEL_POLICY_UNSET;
|
|
|
|
/** @hide */
|
|
@IntDef(prefix = { "PRIORITY_CATEGORY_" }, value = {
|
|
PRIORITY_CATEGORY_REMINDERS,
|
|
PRIORITY_CATEGORY_EVENTS,
|
|
PRIORITY_CATEGORY_MESSAGES,
|
|
PRIORITY_CATEGORY_CALLS,
|
|
PRIORITY_CATEGORY_REPEAT_CALLERS,
|
|
PRIORITY_CATEGORY_ALARMS,
|
|
PRIORITY_CATEGORY_MEDIA,
|
|
PRIORITY_CATEGORY_SYSTEM,
|
|
PRIORITY_CATEGORY_CONVERSATIONS,
|
|
})
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
public @interface PriorityCategory {}
|
|
|
|
/** @hide */
|
|
public static final int PRIORITY_CATEGORY_REMINDERS = 0;
|
|
/** @hide */
|
|
public static final int PRIORITY_CATEGORY_EVENTS = 1;
|
|
/** @hide */
|
|
public static final int PRIORITY_CATEGORY_MESSAGES = 2;
|
|
/** @hide */
|
|
public static final int PRIORITY_CATEGORY_CALLS = 3;
|
|
/** @hide */
|
|
public static final int PRIORITY_CATEGORY_REPEAT_CALLERS = 4;
|
|
/** @hide */
|
|
public static final int PRIORITY_CATEGORY_ALARMS = 5;
|
|
/** @hide */
|
|
public static final int PRIORITY_CATEGORY_MEDIA = 6;
|
|
/** @hide */
|
|
public static final int PRIORITY_CATEGORY_SYSTEM = 7;
|
|
/** @hide */
|
|
public static final int PRIORITY_CATEGORY_CONVERSATIONS = 8;
|
|
|
|
/**
|
|
* Total number of priority categories. Keep updated with any updates to PriorityCategory enum.
|
|
* @hide
|
|
*/
|
|
public static final int NUM_PRIORITY_CATEGORIES = 9;
|
|
|
|
/** @hide */
|
|
@IntDef(prefix = { "VISUAL_EFFECT_" }, value = {
|
|
VISUAL_EFFECT_FULL_SCREEN_INTENT,
|
|
VISUAL_EFFECT_LIGHTS,
|
|
VISUAL_EFFECT_PEEK,
|
|
VISUAL_EFFECT_STATUS_BAR,
|
|
VISUAL_EFFECT_BADGE,
|
|
VISUAL_EFFECT_AMBIENT,
|
|
VISUAL_EFFECT_NOTIFICATION_LIST,
|
|
})
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
public @interface VisualEffect {}
|
|
|
|
/** @hide */
|
|
public static final int VISUAL_EFFECT_FULL_SCREEN_INTENT = 0;
|
|
/** @hide */
|
|
public static final int VISUAL_EFFECT_LIGHTS = 1;
|
|
/** @hide */
|
|
public static final int VISUAL_EFFECT_PEEK = 2;
|
|
/** @hide */
|
|
public static final int VISUAL_EFFECT_STATUS_BAR = 3;
|
|
/** @hide */
|
|
public static final int VISUAL_EFFECT_BADGE = 4;
|
|
/** @hide */
|
|
public static final int VISUAL_EFFECT_AMBIENT = 5;
|
|
/** @hide */
|
|
public static final int VISUAL_EFFECT_NOTIFICATION_LIST = 6;
|
|
|
|
/**
|
|
* Total number of visual effects. Keep updated with any updates to VisualEffect enum.
|
|
* @hide
|
|
*/
|
|
public static final int NUM_VISUAL_EFFECTS = 7;
|
|
|
|
/** @hide */
|
|
@IntDef(prefix = { "PEOPLE_TYPE_" }, value = {
|
|
PEOPLE_TYPE_UNSET,
|
|
PEOPLE_TYPE_ANYONE,
|
|
PEOPLE_TYPE_CONTACTS,
|
|
PEOPLE_TYPE_STARRED,
|
|
PEOPLE_TYPE_NONE,
|
|
})
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
public @interface PeopleType {}
|
|
|
|
/**
|
|
* Used to indicate no preference for the type of people that can bypass dnd for either
|
|
* calls or messages.
|
|
*/
|
|
public static final int PEOPLE_TYPE_UNSET = 0;
|
|
|
|
/**
|
|
* Used to indicate all calls or messages can bypass dnd.
|
|
*/
|
|
public static final int PEOPLE_TYPE_ANYONE = 1;
|
|
|
|
/**
|
|
* Used to indicate calls or messages from contacts can bypass dnd.
|
|
*/
|
|
public static final int PEOPLE_TYPE_CONTACTS = 2;
|
|
|
|
/**
|
|
* Used to indicate calls or messages from starred contacts can bypass dnd.
|
|
*/
|
|
public static final int PEOPLE_TYPE_STARRED = 3;
|
|
|
|
/**
|
|
* Used to indicate no calls or messages can bypass dnd.
|
|
*/
|
|
public static final int PEOPLE_TYPE_NONE = 4;
|
|
|
|
|
|
/** @hide */
|
|
@IntDef(prefix = { "CONVERSATION_SENDERS_" }, value = {
|
|
CONVERSATION_SENDERS_UNSET,
|
|
CONVERSATION_SENDERS_ANYONE,
|
|
CONVERSATION_SENDERS_IMPORTANT,
|
|
CONVERSATION_SENDERS_NONE,
|
|
})
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
public @interface ConversationSenders {}
|
|
|
|
/**
|
|
* Used to indicate no preference for the type of conversations that can bypass dnd.
|
|
*/
|
|
public static final int CONVERSATION_SENDERS_UNSET = 0;
|
|
|
|
/**
|
|
* Used to indicate all conversations can bypass dnd.
|
|
*/
|
|
public static final int CONVERSATION_SENDERS_ANYONE = 1;
|
|
|
|
/**
|
|
* Used to indicate important conversations can bypass dnd.
|
|
*/
|
|
public static final int CONVERSATION_SENDERS_IMPORTANT = 2;
|
|
|
|
/**
|
|
* Used to indicate no conversations can bypass dnd.
|
|
*/
|
|
public static final int CONVERSATION_SENDERS_NONE = 3;
|
|
|
|
/** @hide */
|
|
@IntDef(prefix = { "STATE_" }, value = {
|
|
STATE_UNSET,
|
|
STATE_ALLOW,
|
|
STATE_DISALLOW,
|
|
})
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
public @interface State {}
|
|
|
|
/**
|
|
* Indicates no preference for whether a type of sound or visual effect is or isn't allowed
|
|
* to play/show when DND is active. Will default to the current set policy.
|
|
*/
|
|
public static final int STATE_UNSET = 0;
|
|
|
|
/**
|
|
* Indicates a type of sound or visual effect is allowed to play/show when DND is active.
|
|
*/
|
|
public static final int STATE_ALLOW = 1;
|
|
|
|
/**
|
|
* Indicates a type of sound or visual effect is not allowed to play/show when DND is active.
|
|
*/
|
|
public static final int STATE_DISALLOW = 2;
|
|
|
|
@IntDef(prefix = { "CHANNEL_POLICY_" }, value = {
|
|
CHANNEL_POLICY_UNSET,
|
|
CHANNEL_POLICY_PRIORITY,
|
|
CHANNEL_POLICY_NONE,
|
|
})
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
private @interface ChannelType {}
|
|
|
|
/**
|
|
* Indicates no explicit setting for which channels may bypass DND when this policy is active.
|
|
* Defaults to {@link #CHANNEL_POLICY_PRIORITY}.
|
|
*
|
|
* @hide
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_MODES_API)
|
|
public static final int CHANNEL_POLICY_UNSET = 0;
|
|
|
|
/**
|
|
* Indicates that channels marked as {@link NotificationChannel#canBypassDnd()} can bypass DND
|
|
* when this policy is active.
|
|
*
|
|
* @hide
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_MODES_API)
|
|
public static final int CHANNEL_POLICY_PRIORITY = 1;
|
|
|
|
/**
|
|
* Indicates that no channels can bypass DND when this policy is active, even those marked as
|
|
* {@link NotificationChannel#canBypassDnd()}.
|
|
*
|
|
* @hide
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_MODES_API)
|
|
public static final int CHANNEL_POLICY_NONE = 2;
|
|
|
|
/** @hide */
|
|
public ZenPolicy() {
|
|
mPriorityCategories = new ArrayList<>(Collections.nCopies(NUM_PRIORITY_CATEGORIES, 0));
|
|
mVisualEffects = new ArrayList<>(Collections.nCopies(NUM_VISUAL_EFFECTS, 0));
|
|
}
|
|
|
|
/** @hide */
|
|
@FlaggedApi(Flags.FLAG_MODES_API)
|
|
public ZenPolicy(List<Integer> priorityCategories, List<Integer> visualEffects,
|
|
@PeopleType int priorityMessages, @PeopleType int priorityCalls,
|
|
@ConversationSenders int conversationSenders, @ChannelType int allowChannels) {
|
|
mPriorityCategories = priorityCategories;
|
|
mVisualEffects = visualEffects;
|
|
mPriorityMessages = priorityMessages;
|
|
mPriorityCalls = priorityCalls;
|
|
mConversationSenders = conversationSenders;
|
|
mAllowChannels = allowChannels;
|
|
}
|
|
|
|
/**
|
|
* Conversation type that can bypass DND.
|
|
* @return {@link #CONVERSATION_SENDERS_UNSET}, {@link #CONVERSATION_SENDERS_ANYONE},
|
|
* {@link #CONVERSATION_SENDERS_IMPORTANT}, {@link #CONVERSATION_SENDERS_NONE}.
|
|
*/
|
|
public @ConversationSenders int getPriorityConversationSenders() {
|
|
return mConversationSenders;
|
|
}
|
|
|
|
/**
|
|
* Message senders that can bypass DND.
|
|
* @return {@link #PEOPLE_TYPE_UNSET}, {@link #PEOPLE_TYPE_ANYONE},
|
|
* {@link #PEOPLE_TYPE_CONTACTS}, {@link #PEOPLE_TYPE_STARRED} or {@link #PEOPLE_TYPE_NONE}
|
|
*/
|
|
public @PeopleType int getPriorityMessageSenders() {
|
|
return mPriorityMessages;
|
|
}
|
|
|
|
/**
|
|
* Callers that can bypass DND.
|
|
* @return {@link #PEOPLE_TYPE_UNSET}, {@link #PEOPLE_TYPE_ANYONE},
|
|
* {@link #PEOPLE_TYPE_CONTACTS}, {@link #PEOPLE_TYPE_STARRED} or {@link #PEOPLE_TYPE_NONE}
|
|
*/
|
|
public @PeopleType int getPriorityCallSenders() {
|
|
return mPriorityCalls;
|
|
}
|
|
|
|
/**
|
|
* Whether this policy wants to allow conversation notifications
|
|
* (see {@link NotificationChannel#getConversationId()}) to play sounds and visually appear
|
|
* or to intercept them when DND is active.
|
|
* @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW}
|
|
*/
|
|
public @State int getPriorityCategoryConversations() {
|
|
return mPriorityCategories.get(PRIORITY_CATEGORY_CONVERSATIONS);
|
|
}
|
|
|
|
/**
|
|
* Whether this policy wants to allow notifications with category
|
|
* {@link Notification#CATEGORY_REMINDER} to play sounds and visually appear
|
|
* or to intercept them when DND is active.
|
|
* @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW}
|
|
*/
|
|
public @State int getPriorityCategoryReminders() {
|
|
return mPriorityCategories.get(PRIORITY_CATEGORY_REMINDERS);
|
|
}
|
|
|
|
/**
|
|
* Whether this policy wants to allow notifications with category
|
|
* {@link Notification#CATEGORY_EVENT} to play sounds and visually appear
|
|
* or to intercept them when DND is active.
|
|
* @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW}
|
|
*/
|
|
public @State int getPriorityCategoryEvents() {
|
|
return mPriorityCategories.get(PRIORITY_CATEGORY_EVENTS);
|
|
}
|
|
|
|
/**
|
|
* Whether this policy wants to allow notifications with category
|
|
* {@link Notification#CATEGORY_MESSAGE} to play sounds and visually appear
|
|
* or to intercept them when DND is active. Types of message senders that are allowed
|
|
* are specified by {@link #getPriorityMessageSenders}.
|
|
* @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW}
|
|
*/
|
|
public @State int getPriorityCategoryMessages() {
|
|
return mPriorityCategories.get(PRIORITY_CATEGORY_MESSAGES);
|
|
}
|
|
|
|
/**
|
|
* Whether this policy wants to allow notifications with category
|
|
* {@link Notification#CATEGORY_CALL} to play sounds and visually appear
|
|
* or to intercept them when DND is active. Types of callers that are allowed
|
|
* are specified by {@link #getPriorityCallSenders()}.
|
|
* @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW}
|
|
*/
|
|
public @State int getPriorityCategoryCalls() {
|
|
return mPriorityCategories.get(PRIORITY_CATEGORY_CALLS);
|
|
}
|
|
|
|
/**
|
|
* Whether this policy wants to allow repeat callers (notifications with category
|
|
* {@link Notification#CATEGORY_CALL} that have recently called) to play sounds and
|
|
* visually appear or to intercept them when DND is active.
|
|
* @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW}
|
|
*/
|
|
public @State int getPriorityCategoryRepeatCallers() {
|
|
return mPriorityCategories.get(PRIORITY_CATEGORY_REPEAT_CALLERS);
|
|
}
|
|
|
|
/**
|
|
* Whether this policy wants to allow notifications with category
|
|
* {@link Notification#CATEGORY_ALARM} to play sounds and visually appear
|
|
* or to intercept them when DND is active.
|
|
* When alarms are {@link #STATE_DISALLOW disallowed}, the alarm stream will be muted when DND
|
|
* is active.
|
|
* @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW}
|
|
*/
|
|
public @State int getPriorityCategoryAlarms() {
|
|
return mPriorityCategories.get(PRIORITY_CATEGORY_ALARMS);
|
|
}
|
|
|
|
/**
|
|
* Whether this policy wants to allow media notifications to play sounds and visually appear
|
|
* or to intercept them when DND is active.
|
|
* When media is {@link #STATE_DISALLOW disallowed}, the media stream will be muted when DND is
|
|
* active.
|
|
* @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW}
|
|
*/
|
|
public @State int getPriorityCategoryMedia() {
|
|
return mPriorityCategories.get(PRIORITY_CATEGORY_MEDIA);
|
|
}
|
|
|
|
/**
|
|
* Whether this policy wants to allow system sounds when DND is active.
|
|
* When system is {@link #STATE_DISALLOW}, the system stream will be muted when DND is active.
|
|
* @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW}
|
|
*/
|
|
public @State int getPriorityCategorySystem() {
|
|
return mPriorityCategories.get(PRIORITY_CATEGORY_SYSTEM);
|
|
}
|
|
|
|
/**
|
|
* Whether this policy allows {@link Notification#fullScreenIntent full screen intents} from
|
|
* notifications intercepted by DND.
|
|
*/
|
|
public @State int getVisualEffectFullScreenIntent() {
|
|
return mVisualEffects.get(VISUAL_EFFECT_FULL_SCREEN_INTENT);
|
|
}
|
|
|
|
/**
|
|
* Whether this policy allows {@link NotificationChannel#shouldShowLights() notification
|
|
* lights} from notifications intercepted by DND.
|
|
*/
|
|
public @State int getVisualEffectLights() {
|
|
return mVisualEffects.get(VISUAL_EFFECT_LIGHTS);
|
|
}
|
|
|
|
/**
|
|
* Whether this policy allows peeking from notifications intercepted by DND.
|
|
*/
|
|
public @State int getVisualEffectPeek() {
|
|
return mVisualEffects.get(VISUAL_EFFECT_PEEK);
|
|
}
|
|
|
|
/**
|
|
* Whether this policy allows notifications intercepted by DND from appearing in the status bar
|
|
* on devices that support status bars.
|
|
*/
|
|
public @State int getVisualEffectStatusBar() {
|
|
return mVisualEffects.get(VISUAL_EFFECT_STATUS_BAR);
|
|
}
|
|
|
|
/**
|
|
* Whether this policy allows {@link NotificationChannel#canShowBadge() badges} from
|
|
* notifications intercepted by DND on devices that support badging.
|
|
*/
|
|
public @State int getVisualEffectBadge() {
|
|
return mVisualEffects.get(VISUAL_EFFECT_BADGE);
|
|
}
|
|
|
|
/**
|
|
* Whether this policy allows notifications intercepted by DND from appearing on ambient
|
|
* displays on devices that support ambient display.
|
|
*/
|
|
public @State int getVisualEffectAmbient() {
|
|
return mVisualEffects.get(VISUAL_EFFECT_AMBIENT);
|
|
}
|
|
|
|
/**
|
|
* Whether this policy allows notifications intercepted by DND from appearing in notification
|
|
* list views like the notification shade or lockscreen on devices that support those
|
|
* views.
|
|
*/
|
|
public @State int getVisualEffectNotificationList() {
|
|
return mVisualEffects.get(VISUAL_EFFECT_NOTIFICATION_LIST);
|
|
}
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
public @ChannelType int getAllowedChannels() {
|
|
return mAllowChannels;
|
|
}
|
|
|
|
/**
|
|
* Whether this policy allows {@link NotificationChannel channels} marked as
|
|
* {@link NotificationChannel#canBypassDnd()} to bypass DND. If {@link #STATE_ALLOW}, these
|
|
* channels may bypass; if {@link #STATE_DISALLOW}, then even notifications from channels
|
|
* with {@link NotificationChannel#canBypassDnd()} will be intercepted.
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_MODES_API)
|
|
public @State int getPriorityChannelsAllowed() {
|
|
switch (mAllowChannels) {
|
|
case CHANNEL_POLICY_PRIORITY:
|
|
return STATE_ALLOW;
|
|
case CHANNEL_POLICY_NONE:
|
|
return STATE_DISALLOW;
|
|
default:
|
|
return STATE_UNSET;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Whether this policy hides all visual effects
|
|
* @hide
|
|
*/
|
|
public boolean shouldHideAllVisualEffects() {
|
|
for (int i = 0; i < mVisualEffects.size(); i++) {
|
|
if (mVisualEffects.get(i) != STATE_DISALLOW) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Whether this policy shows all visual effects
|
|
* @hide
|
|
*/
|
|
public boolean shouldShowAllVisualEffects() {
|
|
for (int i = 0; i < mVisualEffects.size(); i++) {
|
|
if (mVisualEffects.get(i) != STATE_ALLOW) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Builder class for {@link ZenPolicy} objects.
|
|
* Provides a convenient way to set the various fields of a {@link ZenPolicy}. If a field
|
|
* is not set, it is (@link STATE_UNSET} and will not change the current set policy.
|
|
*/
|
|
public static final class Builder {
|
|
private ZenPolicy mZenPolicy;
|
|
|
|
public Builder() {
|
|
mZenPolicy = new ZenPolicy();
|
|
}
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
@SuppressLint("UnflaggedApi")
|
|
@TestApi
|
|
public Builder(@Nullable ZenPolicy policy) {
|
|
if (policy != null) {
|
|
mZenPolicy = policy.copy();
|
|
} else {
|
|
mZenPolicy = new ZenPolicy();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Builds the current ZenPolicy.
|
|
*/
|
|
public @NonNull ZenPolicy build() {
|
|
if (Flags.modesApi()) {
|
|
return new ZenPolicy(new ArrayList<>(mZenPolicy.mPriorityCategories),
|
|
new ArrayList<>(mZenPolicy.mVisualEffects),
|
|
mZenPolicy.mPriorityMessages, mZenPolicy.mPriorityCalls,
|
|
mZenPolicy.mConversationSenders, mZenPolicy.mAllowChannels);
|
|
} else {
|
|
return mZenPolicy.copy();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Allows all notifications to bypass DND and unmutes all streams.
|
|
*/
|
|
public @NonNull Builder allowAllSounds() {
|
|
for (int i = 0; i < mZenPolicy.mPriorityCategories.size(); i++) {
|
|
mZenPolicy.mPriorityCategories.set(i, STATE_ALLOW);
|
|
}
|
|
mZenPolicy.mPriorityMessages = PEOPLE_TYPE_ANYONE;
|
|
mZenPolicy.mPriorityCalls = PEOPLE_TYPE_ANYONE;
|
|
mZenPolicy.mConversationSenders = CONVERSATION_SENDERS_ANYONE;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Intercepts all notifications and prevents them from playing sounds
|
|
* when DND is active. Also mutes alarm, system and media streams.
|
|
* Notification channels can still play sounds only if they
|
|
* {@link NotificationChannel#canBypassDnd can bypass DND}. If no channels can bypass DND,
|
|
* the ringer stream is also muted.
|
|
*/
|
|
public @NonNull Builder disallowAllSounds() {
|
|
for (int i = 0; i < mZenPolicy.mPriorityCategories.size(); i++) {
|
|
mZenPolicy.mPriorityCategories.set(i, STATE_DISALLOW);
|
|
}
|
|
mZenPolicy.mPriorityMessages = PEOPLE_TYPE_NONE;
|
|
mZenPolicy.mPriorityCalls = PEOPLE_TYPE_NONE;
|
|
mZenPolicy.mConversationSenders = CONVERSATION_SENDERS_NONE;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Allows notifications intercepted by DND to show on all surfaces when DND is active.
|
|
*/
|
|
public @NonNull Builder showAllVisualEffects() {
|
|
for (int i = 0; i < mZenPolicy.mVisualEffects.size(); i++) {
|
|
mZenPolicy.mVisualEffects.set(i, STATE_ALLOW);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Disallows notifications intercepted by DND from showing when DND is active.
|
|
*/
|
|
public @NonNull Builder hideAllVisualEffects() {
|
|
for (int i = 0; i < mZenPolicy.mVisualEffects.size(); i++) {
|
|
mZenPolicy.mVisualEffects.set(i, STATE_DISALLOW);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Unsets a priority category, neither allowing or disallowing. When applying this policy,
|
|
* unset categories will default to the current applied policy.
|
|
* @hide
|
|
*/
|
|
public @NonNull Builder unsetPriorityCategory(@PriorityCategory int category) {
|
|
mZenPolicy.mPriorityCategories.set(category, STATE_UNSET);
|
|
|
|
if (category == PRIORITY_CATEGORY_MESSAGES) {
|
|
mZenPolicy.mPriorityMessages = PEOPLE_TYPE_UNSET;
|
|
} else if (category == PRIORITY_CATEGORY_CALLS) {
|
|
mZenPolicy.mPriorityCalls = PEOPLE_TYPE_UNSET;
|
|
} else if (category == PRIORITY_CATEGORY_CONVERSATIONS) {
|
|
mZenPolicy.mConversationSenders = CONVERSATION_SENDERS_UNSET;
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Unsets a visual effect, neither allowing or disallowing. When applying this policy,
|
|
* unset effects will default to the current applied policy.
|
|
* @hide
|
|
*/
|
|
public @NonNull Builder unsetVisualEffect(@VisualEffect int effect) {
|
|
mZenPolicy.mVisualEffects.set(effect, STATE_UNSET);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Whether to allow notifications with category {@link Notification#CATEGORY_REMINDER}
|
|
* to play sounds and visually appear or to intercept them when DND is active.
|
|
*/
|
|
public @NonNull Builder allowReminders(boolean allow) {
|
|
mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_REMINDERS,
|
|
allow ? STATE_ALLOW : STATE_DISALLOW);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Whether to allow notifications with category {@link Notification#CATEGORY_EVENT}
|
|
* to play sounds and visually appear or to intercept them when DND is active.
|
|
*/
|
|
public @NonNull Builder allowEvents(boolean allow) {
|
|
mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_EVENTS,
|
|
allow ? STATE_ALLOW : STATE_DISALLOW);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Whether to allow conversation notifications
|
|
* (see {@link NotificationChannel#setConversationId(String, String)})
|
|
* that match audienceType to play sounds and visually appear or to intercept
|
|
* them when DND is active.
|
|
* @param audienceType callers that are allowed to bypass DND
|
|
*/
|
|
public @NonNull Builder allowConversations(@ConversationSenders int audienceType) {
|
|
if (audienceType == STATE_UNSET) {
|
|
return unsetPriorityCategory(PRIORITY_CATEGORY_CONVERSATIONS);
|
|
}
|
|
|
|
if (audienceType == CONVERSATION_SENDERS_NONE) {
|
|
mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_CONVERSATIONS, STATE_DISALLOW);
|
|
} else if (audienceType == CONVERSATION_SENDERS_ANYONE
|
|
|| audienceType == CONVERSATION_SENDERS_IMPORTANT) {
|
|
mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_CONVERSATIONS, STATE_ALLOW);
|
|
} else {
|
|
return this;
|
|
}
|
|
|
|
mZenPolicy.mConversationSenders = audienceType;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Whether to allow notifications with category {@link Notification#CATEGORY_MESSAGE}
|
|
* that match audienceType to play sounds and visually appear or to intercept
|
|
* them when DND is active.
|
|
* @param audienceType message senders that are allowed to bypass DND
|
|
*/
|
|
public @NonNull Builder allowMessages(@PeopleType int audienceType) {
|
|
if (audienceType == STATE_UNSET) {
|
|
return unsetPriorityCategory(PRIORITY_CATEGORY_MESSAGES);
|
|
}
|
|
|
|
if (audienceType == PEOPLE_TYPE_NONE) {
|
|
mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_MESSAGES, STATE_DISALLOW);
|
|
} else if (audienceType == PEOPLE_TYPE_ANYONE || audienceType == PEOPLE_TYPE_CONTACTS
|
|
|| audienceType == PEOPLE_TYPE_STARRED) {
|
|
mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_MESSAGES, STATE_ALLOW);
|
|
} else {
|
|
return this;
|
|
}
|
|
|
|
mZenPolicy.mPriorityMessages = audienceType;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Whether to allow notifications with category {@link Notification#CATEGORY_CALL}
|
|
* that match audienceType to play sounds and visually appear or to intercept
|
|
* them when DND is active.
|
|
* @param audienceType callers that are allowed to bypass DND
|
|
*/
|
|
public @NonNull Builder allowCalls(@PeopleType int audienceType) {
|
|
if (audienceType == STATE_UNSET) {
|
|
return unsetPriorityCategory(PRIORITY_CATEGORY_CALLS);
|
|
}
|
|
|
|
if (audienceType == PEOPLE_TYPE_NONE) {
|
|
mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_CALLS, STATE_DISALLOW);
|
|
} else if (audienceType == PEOPLE_TYPE_ANYONE || audienceType == PEOPLE_TYPE_CONTACTS
|
|
|| audienceType == PEOPLE_TYPE_STARRED) {
|
|
mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_CALLS, STATE_ALLOW);
|
|
} else {
|
|
return this;
|
|
}
|
|
|
|
mZenPolicy.mPriorityCalls = audienceType;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Whether to allow repeat callers (notifications with category
|
|
* {@link Notification#CATEGORY_CALL} that have recently called
|
|
* to play sounds and visually appear.
|
|
*/
|
|
public @NonNull Builder allowRepeatCallers(boolean allow) {
|
|
mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_REPEAT_CALLERS,
|
|
allow ? STATE_ALLOW : STATE_DISALLOW);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Whether to allow notifications with category {@link Notification#CATEGORY_ALARM}
|
|
* to play sounds and visually appear or to intercept them when DND is active.
|
|
* Disallowing alarms will mute the alarm stream when DND is active.
|
|
*/
|
|
public @NonNull Builder allowAlarms(boolean allow) {
|
|
mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_ALARMS,
|
|
allow ? STATE_ALLOW : STATE_DISALLOW);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Whether to allow media notifications to play sounds and visually
|
|
* appear or to intercept them when DND is active.
|
|
* Disallowing media will mute the media stream when DND is active.
|
|
*/
|
|
public @NonNull Builder allowMedia(boolean allow) {
|
|
mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_MEDIA,
|
|
allow ? STATE_ALLOW : STATE_DISALLOW);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Whether to allow system sounds to play when DND is active.
|
|
* Disallowing system sounds will mute the system stream when DND is active.
|
|
*/
|
|
public @NonNull Builder allowSystem(boolean allow) {
|
|
mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_SYSTEM,
|
|
allow ? STATE_ALLOW : STATE_DISALLOW);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Whether to allow {@link PriorityCategory} sounds to play when DND is active.
|
|
* @hide
|
|
*/
|
|
public @NonNull Builder allowCategory(@PriorityCategory int category, boolean allow) {
|
|
switch (category) {
|
|
case PRIORITY_CATEGORY_ALARMS:
|
|
allowAlarms(allow);
|
|
break;
|
|
case PRIORITY_CATEGORY_MEDIA:
|
|
allowMedia(allow);
|
|
break;
|
|
case PRIORITY_CATEGORY_SYSTEM:
|
|
allowSystem(allow);
|
|
break;
|
|
case PRIORITY_CATEGORY_REMINDERS:
|
|
allowReminders(allow);
|
|
break;
|
|
case PRIORITY_CATEGORY_EVENTS:
|
|
allowEvents(allow);
|
|
break;
|
|
case PRIORITY_CATEGORY_REPEAT_CALLERS:
|
|
allowRepeatCallers(allow);
|
|
break;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Whether {@link Notification#fullScreenIntent full screen intents} that are intercepted
|
|
* by DND are shown.
|
|
*/
|
|
public @NonNull Builder showFullScreenIntent(boolean show) {
|
|
mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_FULL_SCREEN_INTENT,
|
|
show ? STATE_ALLOW : STATE_DISALLOW);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Whether {@link NotificationChannel#shouldShowLights() notification lights} from
|
|
* notifications intercepted by DND are blocked.
|
|
*/
|
|
public @NonNull Builder showLights(boolean show) {
|
|
mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_LIGHTS,
|
|
show ? STATE_ALLOW : STATE_DISALLOW);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Whether notifications intercepted by DND are prevented from peeking.
|
|
*/
|
|
public @NonNull Builder showPeeking(boolean show) {
|
|
mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_PEEK,
|
|
show ? STATE_ALLOW : STATE_DISALLOW);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Whether notifications intercepted by DND are prevented from appearing in the status bar
|
|
* on devices that support status bars.
|
|
*/
|
|
public @NonNull Builder showStatusBarIcons(boolean show) {
|
|
mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_STATUS_BAR,
|
|
show ? STATE_ALLOW : STATE_DISALLOW);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Whether {@link NotificationChannel#canShowBadge() badges} from
|
|
* notifications intercepted by DND are allowed on devices that support badging.
|
|
*/
|
|
public @NonNull Builder showBadges(boolean show) {
|
|
mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_BADGE,
|
|
show ? STATE_ALLOW : STATE_DISALLOW);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Whether notification intercepted by DND are prevented from appearing on ambient displays
|
|
* on devices that support ambient display.
|
|
*/
|
|
public @NonNull Builder showInAmbientDisplay(boolean show) {
|
|
mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_AMBIENT,
|
|
show ? STATE_ALLOW : STATE_DISALLOW);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Whether notification intercepted by DND are prevented from appearing in notification
|
|
* list views like the notification shade or lockscreen on devices that support those
|
|
* views.
|
|
*/
|
|
public @NonNull Builder showInNotificationList(boolean show) {
|
|
mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_NOTIFICATION_LIST,
|
|
show ? STATE_ALLOW : STATE_DISALLOW);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Whether notifications intercepted by DND are prevented from appearing for
|
|
* {@link VisualEffect}
|
|
* @hide
|
|
*/
|
|
public @NonNull Builder showVisualEffect(@VisualEffect int effect, boolean show) {
|
|
switch (effect) {
|
|
case VISUAL_EFFECT_FULL_SCREEN_INTENT:
|
|
showFullScreenIntent(show);
|
|
break;
|
|
case VISUAL_EFFECT_LIGHTS:
|
|
showLights(show);
|
|
break;
|
|
case VISUAL_EFFECT_PEEK:
|
|
showPeeking(show);
|
|
break;
|
|
case VISUAL_EFFECT_STATUS_BAR:
|
|
showStatusBarIcons(show);
|
|
break;
|
|
case VISUAL_EFFECT_BADGE:
|
|
showBadges(show);
|
|
break;
|
|
case VISUAL_EFFECT_AMBIENT:
|
|
showInAmbientDisplay(show);
|
|
break;
|
|
case VISUAL_EFFECT_NOTIFICATION_LIST:
|
|
showInNotificationList(show);
|
|
break;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Set whether priority channels are permitted to break through DND.
|
|
*/
|
|
@SuppressLint("BuilderSetStyle")
|
|
@FlaggedApi(Flags.FLAG_MODES_API)
|
|
public @NonNull Builder allowPriorityChannels(boolean allow) {
|
|
mZenPolicy.mAllowChannels = allow ? CHANNEL_POLICY_PRIORITY : CHANNEL_POLICY_NONE;
|
|
return this;
|
|
}
|
|
|
|
/** @hide */
|
|
public @NonNull Builder allowChannels(@ChannelType int channelType) {
|
|
mZenPolicy.mAllowChannels = channelType;
|
|
return this;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int describeContents() {
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
public void writeToParcel(Parcel dest, int flags) {
|
|
dest.writeList(mPriorityCategories);
|
|
dest.writeList(mVisualEffects);
|
|
dest.writeInt(mPriorityMessages);
|
|
dest.writeInt(mPriorityCalls);
|
|
dest.writeInt(mConversationSenders);
|
|
if (Flags.modesApi()) {
|
|
dest.writeInt(mAllowChannels);
|
|
}
|
|
}
|
|
|
|
public static final @NonNull Creator<ZenPolicy> CREATOR =
|
|
new Creator<ZenPolicy>() {
|
|
@Override
|
|
public ZenPolicy createFromParcel(Parcel source) {
|
|
ZenPolicy policy;
|
|
if (Flags.modesApi()) {
|
|
policy = new ZenPolicy(
|
|
trimList(source.readArrayList(Integer.class.getClassLoader(),
|
|
Integer.class), NUM_PRIORITY_CATEGORIES),
|
|
trimList(source.readArrayList(Integer.class.getClassLoader(),
|
|
Integer.class), NUM_VISUAL_EFFECTS),
|
|
source.readInt(), source.readInt(), source.readInt(),
|
|
source.readInt()
|
|
);
|
|
} else {
|
|
policy = new ZenPolicy();
|
|
policy.mPriorityCategories =
|
|
trimList(source.readArrayList(Integer.class.getClassLoader(),
|
|
Integer.class), NUM_PRIORITY_CATEGORIES);
|
|
policy.mVisualEffects =
|
|
trimList(source.readArrayList(Integer.class.getClassLoader(),
|
|
Integer.class), NUM_VISUAL_EFFECTS);
|
|
policy.mPriorityMessages = source.readInt();
|
|
policy.mPriorityCalls = source.readInt();
|
|
policy.mConversationSenders = source.readInt();
|
|
}
|
|
return policy;
|
|
}
|
|
|
|
@Override
|
|
public ZenPolicy[] newArray(int size) {
|
|
return new ZenPolicy[size];
|
|
}
|
|
};
|
|
|
|
@Override
|
|
public String toString() {
|
|
StringBuilder sb = new StringBuilder(ZenPolicy.class.getSimpleName())
|
|
.append('{')
|
|
.append("priorityCategories=[").append(priorityCategoriesToString())
|
|
.append("], visualEffects=[").append(visualEffectsToString())
|
|
.append("], priorityCallsSenders=").append(peopleTypeToString(mPriorityCalls))
|
|
.append(", priorityMessagesSenders=").append(peopleTypeToString(mPriorityMessages))
|
|
.append(", priorityConversationSenders=").append(
|
|
conversationTypeToString(mConversationSenders));
|
|
if (Flags.modesApi()) {
|
|
sb.append(", allowChannels=").append(channelTypeToString(mAllowChannels));
|
|
}
|
|
return sb.append('}').toString();
|
|
}
|
|
|
|
/** @hide */
|
|
public static String fieldsToString(@ModifiableField int bitmask) {
|
|
ArrayList<String> modified = new ArrayList<>();
|
|
if ((bitmask & FIELD_MESSAGES) != 0) {
|
|
modified.add("FIELD_MESSAGES");
|
|
}
|
|
if ((bitmask & FIELD_CALLS) != 0) {
|
|
modified.add("FIELD_CALLS");
|
|
}
|
|
if ((bitmask & FIELD_CONVERSATIONS) != 0) {
|
|
modified.add("FIELD_CONVERSATIONS");
|
|
}
|
|
if ((bitmask & FIELD_ALLOW_CHANNELS) != 0) {
|
|
modified.add("FIELD_ALLOW_CHANNELS");
|
|
}
|
|
if ((bitmask & FIELD_PRIORITY_CATEGORY_REMINDERS) != 0) {
|
|
modified.add("FIELD_PRIORITY_CATEGORY_REMINDERS");
|
|
}
|
|
if ((bitmask & FIELD_PRIORITY_CATEGORY_EVENTS) != 0) {
|
|
modified.add("FIELD_PRIORITY_CATEGORY_EVENTS");
|
|
}
|
|
if ((bitmask & FIELD_PRIORITY_CATEGORY_REPEAT_CALLERS) != 0) {
|
|
modified.add("FIELD_PRIORITY_CATEGORY_REPEAT_CALLERS");
|
|
}
|
|
if ((bitmask & FIELD_PRIORITY_CATEGORY_ALARMS) != 0) {
|
|
modified.add("FIELD_PRIORITY_CATEGORY_ALARMS");
|
|
}
|
|
if ((bitmask & FIELD_PRIORITY_CATEGORY_MEDIA) != 0) {
|
|
modified.add("FIELD_PRIORITY_CATEGORY_MEDIA");
|
|
}
|
|
if ((bitmask & FIELD_PRIORITY_CATEGORY_SYSTEM) != 0) {
|
|
modified.add("FIELD_PRIORITY_CATEGORY_SYSTEM");
|
|
}
|
|
if ((bitmask & FIELD_VISUAL_EFFECT_FULL_SCREEN_INTENT) != 0) {
|
|
modified.add("FIELD_VISUAL_EFFECT_FULL_SCREEN_INTENT");
|
|
}
|
|
if ((bitmask & FIELD_VISUAL_EFFECT_LIGHTS) != 0) {
|
|
modified.add("FIELD_VISUAL_EFFECT_LIGHTS");
|
|
}
|
|
if ((bitmask & FIELD_VISUAL_EFFECT_PEEK) != 0) {
|
|
modified.add("FIELD_VISUAL_EFFECT_PEEK");
|
|
}
|
|
if ((bitmask & FIELD_VISUAL_EFFECT_STATUS_BAR) != 0) {
|
|
modified.add("FIELD_VISUAL_EFFECT_STATUS_BAR");
|
|
}
|
|
if ((bitmask & FIELD_VISUAL_EFFECT_BADGE) != 0) {
|
|
modified.add("FIELD_VISUAL_EFFECT_BADGE");
|
|
}
|
|
if ((bitmask & FIELD_VISUAL_EFFECT_AMBIENT) != 0) {
|
|
modified.add("FIELD_VISUAL_EFFECT_AMBIENT");
|
|
}
|
|
if ((bitmask & FIELD_VISUAL_EFFECT_NOTIFICATION_LIST) != 0) {
|
|
modified.add("FIELD_VISUAL_EFFECT_NOTIFICATION_LIST");
|
|
}
|
|
return "{" + String.join(",", modified) + "}";
|
|
}
|
|
|
|
// Returns a list containing the first maxLength elements of the input list if the list is
|
|
// longer than that size. For the lists in ZenPolicy, this should not happen unless the input
|
|
// is corrupt.
|
|
private static ArrayList<Integer> trimList(ArrayList<Integer> list, int maxLength) {
|
|
if (list == null || list.size() <= maxLength) {
|
|
return list;
|
|
}
|
|
return new ArrayList<>(list.subList(0, maxLength));
|
|
}
|
|
|
|
private String priorityCategoriesToString() {
|
|
StringBuilder builder = new StringBuilder();
|
|
for (int i = 0; i < mPriorityCategories.size(); i++) {
|
|
if (mPriorityCategories.get(i) != STATE_UNSET) {
|
|
builder.append(indexToCategory(i))
|
|
.append("=")
|
|
.append(stateToString(mPriorityCategories.get(i)))
|
|
.append(" ");
|
|
}
|
|
|
|
}
|
|
return builder.toString();
|
|
}
|
|
|
|
private String visualEffectsToString() {
|
|
StringBuilder builder = new StringBuilder();
|
|
for (int i = 0; i < mVisualEffects.size(); i++) {
|
|
if (mVisualEffects.get(i) != STATE_UNSET) {
|
|
builder.append(indexToVisualEffect(i))
|
|
.append("=")
|
|
.append(stateToString(mVisualEffects.get(i)))
|
|
.append(" ");
|
|
}
|
|
|
|
}
|
|
return builder.toString();
|
|
}
|
|
|
|
private String indexToVisualEffect(@VisualEffect int visualEffectIndex) {
|
|
switch (visualEffectIndex) {
|
|
case VISUAL_EFFECT_FULL_SCREEN_INTENT:
|
|
return "fullScreenIntent";
|
|
case VISUAL_EFFECT_LIGHTS:
|
|
return "lights";
|
|
case VISUAL_EFFECT_PEEK:
|
|
return "peek";
|
|
case VISUAL_EFFECT_STATUS_BAR:
|
|
return "statusBar";
|
|
case VISUAL_EFFECT_BADGE:
|
|
return "badge";
|
|
case VISUAL_EFFECT_AMBIENT:
|
|
return "ambient";
|
|
case VISUAL_EFFECT_NOTIFICATION_LIST:
|
|
return "notificationList";
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private String indexToCategory(@PriorityCategory int categoryIndex) {
|
|
switch (categoryIndex) {
|
|
case PRIORITY_CATEGORY_REMINDERS:
|
|
return "reminders";
|
|
case PRIORITY_CATEGORY_EVENTS:
|
|
return "events";
|
|
case PRIORITY_CATEGORY_MESSAGES:
|
|
return "messages";
|
|
case PRIORITY_CATEGORY_CALLS:
|
|
return "calls";
|
|
case PRIORITY_CATEGORY_REPEAT_CALLERS:
|
|
return "repeatCallers";
|
|
case PRIORITY_CATEGORY_ALARMS:
|
|
return "alarms";
|
|
case PRIORITY_CATEGORY_MEDIA:
|
|
return "media";
|
|
case PRIORITY_CATEGORY_SYSTEM:
|
|
return "system";
|
|
case PRIORITY_CATEGORY_CONVERSATIONS:
|
|
return "convs";
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private String stateToString(@State int state) {
|
|
switch (state) {
|
|
case STATE_UNSET:
|
|
return "unset";
|
|
case STATE_DISALLOW:
|
|
return "disallow";
|
|
case STATE_ALLOW:
|
|
return "allow";
|
|
}
|
|
return "invalidState{" + state + "}";
|
|
}
|
|
|
|
private String peopleTypeToString(@PeopleType int peopleType) {
|
|
switch (peopleType) {
|
|
case PEOPLE_TYPE_ANYONE:
|
|
return "anyone";
|
|
case PEOPLE_TYPE_CONTACTS:
|
|
return "contacts";
|
|
case PEOPLE_TYPE_NONE:
|
|
return "none";
|
|
case PEOPLE_TYPE_STARRED:
|
|
return "starred_contacts";
|
|
case STATE_UNSET:
|
|
return "unset";
|
|
}
|
|
return "invalidPeopleType{" + peopleType + "}";
|
|
}
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
public static String conversationTypeToString(@ConversationSenders int conversationType) {
|
|
switch (conversationType) {
|
|
case CONVERSATION_SENDERS_ANYONE:
|
|
return "anyone";
|
|
case CONVERSATION_SENDERS_IMPORTANT:
|
|
return "important";
|
|
case CONVERSATION_SENDERS_NONE:
|
|
return "none";
|
|
case CONVERSATION_SENDERS_UNSET:
|
|
return "unset";
|
|
}
|
|
return "invalidConversationType{" + conversationType + "}";
|
|
}
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_MODES_API)
|
|
public static String channelTypeToString(@ChannelType int channelType) {
|
|
switch (channelType) {
|
|
case CHANNEL_POLICY_UNSET:
|
|
return "unset";
|
|
case CHANNEL_POLICY_PRIORITY:
|
|
return "priority";
|
|
case CHANNEL_POLICY_NONE:
|
|
return "none";
|
|
}
|
|
return "invalidChannelType{" + channelType + "}";
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(@Nullable Object o) {
|
|
if (!(o instanceof ZenPolicy)) return false;
|
|
if (o == this) return true;
|
|
final ZenPolicy other = (ZenPolicy) o;
|
|
|
|
boolean eq = Objects.equals(other.mPriorityCategories, mPriorityCategories)
|
|
&& Objects.equals(other.mVisualEffects, mVisualEffects)
|
|
&& other.mPriorityCalls == mPriorityCalls
|
|
&& other.mPriorityMessages == mPriorityMessages
|
|
&& other.mConversationSenders == mConversationSenders;
|
|
if (Flags.modesApi()) {
|
|
return eq && other.mAllowChannels == mAllowChannels;
|
|
}
|
|
return eq;
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
if (Flags.modesApi()) {
|
|
return Objects.hash(mPriorityCategories, mVisualEffects, mPriorityCalls,
|
|
mPriorityMessages, mConversationSenders, mAllowChannels);
|
|
}
|
|
return Objects.hash(mPriorityCategories, mVisualEffects, mPriorityCalls, mPriorityMessages,
|
|
mConversationSenders);
|
|
}
|
|
|
|
private @State int getZenPolicyPriorityCategoryState(@PriorityCategory int
|
|
category) {
|
|
switch (category) {
|
|
case PRIORITY_CATEGORY_REMINDERS:
|
|
return getPriorityCategoryReminders();
|
|
case PRIORITY_CATEGORY_EVENTS:
|
|
return getPriorityCategoryEvents();
|
|
case PRIORITY_CATEGORY_MESSAGES:
|
|
return getPriorityCategoryMessages();
|
|
case PRIORITY_CATEGORY_CALLS:
|
|
return getPriorityCategoryCalls();
|
|
case PRIORITY_CATEGORY_REPEAT_CALLERS:
|
|
return getPriorityCategoryRepeatCallers();
|
|
case PRIORITY_CATEGORY_ALARMS:
|
|
return getPriorityCategoryAlarms();
|
|
case PRIORITY_CATEGORY_MEDIA:
|
|
return getPriorityCategoryMedia();
|
|
case PRIORITY_CATEGORY_SYSTEM:
|
|
return getPriorityCategorySystem();
|
|
case PRIORITY_CATEGORY_CONVERSATIONS:
|
|
return getPriorityCategoryConversations();
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
private @State int getZenPolicyVisualEffectState(@VisualEffect int effect) {
|
|
switch (effect) {
|
|
case VISUAL_EFFECT_FULL_SCREEN_INTENT:
|
|
return getVisualEffectFullScreenIntent();
|
|
case VISUAL_EFFECT_LIGHTS:
|
|
return getVisualEffectLights();
|
|
case VISUAL_EFFECT_PEEK:
|
|
return getVisualEffectPeek();
|
|
case VISUAL_EFFECT_STATUS_BAR:
|
|
return getVisualEffectStatusBar();
|
|
case VISUAL_EFFECT_BADGE:
|
|
return getVisualEffectBadge();
|
|
case VISUAL_EFFECT_AMBIENT:
|
|
return getVisualEffectAmbient();
|
|
case VISUAL_EFFECT_NOTIFICATION_LIST:
|
|
return getVisualEffectNotificationList();
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/** @hide */
|
|
public static boolean stateToBoolean(@State int state, boolean defaultVal) {
|
|
switch (state) {
|
|
case STATE_ALLOW:
|
|
return true;
|
|
case STATE_DISALLOW:
|
|
return false;
|
|
default:
|
|
return defaultVal;
|
|
}
|
|
}
|
|
|
|
/** @hide */
|
|
public boolean isCategoryAllowed(@PriorityCategory int category, boolean defaultVal) {
|
|
return stateToBoolean(getZenPolicyPriorityCategoryState(category), defaultVal);
|
|
}
|
|
|
|
/** @hide */
|
|
public boolean isVisualEffectAllowed(@VisualEffect int effect, boolean defaultVal) {
|
|
return stateToBoolean(getZenPolicyVisualEffectState(effect), defaultVal);
|
|
}
|
|
|
|
/**
|
|
* Applies another policy on top of this policy. For each field, the resulting policy will have
|
|
* most restrictive setting that is set of the two policies (if only one has a field set, the
|
|
* result will inherit that policy's setting).
|
|
*
|
|
* @hide
|
|
*/
|
|
public void apply(ZenPolicy policyToApply) {
|
|
if (policyToApply == null) {
|
|
return;
|
|
}
|
|
|
|
// apply priority categories
|
|
for (int category = 0; category < mPriorityCategories.size(); category++) {
|
|
if (mPriorityCategories.get(category) == STATE_DISALLOW) {
|
|
// if a priority category is already disallowed by the policy, cannot allow
|
|
continue;
|
|
}
|
|
|
|
@State int newState = policyToApply.mPriorityCategories.get(category);
|
|
if (newState != STATE_UNSET) {
|
|
mPriorityCategories.set(category, newState);
|
|
|
|
if (category == PRIORITY_CATEGORY_MESSAGES
|
|
&& mPriorityMessages < policyToApply.mPriorityMessages) {
|
|
mPriorityMessages = policyToApply.mPriorityMessages;
|
|
} else if (category == PRIORITY_CATEGORY_CALLS
|
|
&& mPriorityCalls < policyToApply.mPriorityCalls) {
|
|
mPriorityCalls = policyToApply.mPriorityCalls;
|
|
} else if (category == PRIORITY_CATEGORY_CONVERSATIONS
|
|
&& mConversationSenders < policyToApply.mConversationSenders) {
|
|
mConversationSenders = policyToApply.mConversationSenders;
|
|
}
|
|
}
|
|
}
|
|
|
|
// apply visual effects
|
|
for (int visualEffect = 0; visualEffect < mVisualEffects.size(); visualEffect++) {
|
|
if (mVisualEffects.get(visualEffect) == STATE_DISALLOW) {
|
|
// if a visual effect is already disallowed by the policy, cannot allow
|
|
continue;
|
|
}
|
|
|
|
if (policyToApply.mVisualEffects.get(visualEffect) != STATE_UNSET) {
|
|
mVisualEffects.set(visualEffect, policyToApply.mVisualEffects.get(visualEffect));
|
|
}
|
|
}
|
|
|
|
// apply allowed channels
|
|
if (Flags.modesApi()) {
|
|
// if no channels are allowed, can't newly allow them
|
|
if (mAllowChannels != CHANNEL_POLICY_NONE
|
|
&& policyToApply.mAllowChannels != CHANNEL_POLICY_UNSET) {
|
|
mAllowChannels = policyToApply.mAllowChannels;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Overwrites any policy values in this ZenPolicy with set values from newPolicy and
|
|
* returns a copy of the resulting ZenPolicy.
|
|
* Unlike apply(), values set in newPolicy will always be kept over pre-existing
|
|
* fields. Any values in newPolicy that are not set keep their currently set values.
|
|
*
|
|
* @hide
|
|
*/
|
|
@TestApi
|
|
@FlaggedApi(Flags.FLAG_MODES_API)
|
|
public @NonNull ZenPolicy overwrittenWith(@Nullable ZenPolicy newPolicy) {
|
|
ZenPolicy result = this.copy();
|
|
|
|
if (newPolicy == null) {
|
|
return result;
|
|
}
|
|
|
|
// set priority categories
|
|
for (int category = 0; category < mPriorityCategories.size(); category++) {
|
|
@State int newState = newPolicy.mPriorityCategories.get(category);
|
|
if (newState != STATE_UNSET) {
|
|
result.mPriorityCategories.set(category, newState);
|
|
|
|
if (category == PRIORITY_CATEGORY_MESSAGES) {
|
|
result.mPriorityMessages = newPolicy.mPriorityMessages;
|
|
} else if (category == PRIORITY_CATEGORY_CALLS) {
|
|
result.mPriorityCalls = newPolicy.mPriorityCalls;
|
|
} else if (category == PRIORITY_CATEGORY_CONVERSATIONS) {
|
|
result.mConversationSenders = newPolicy.mConversationSenders;
|
|
}
|
|
}
|
|
}
|
|
|
|
// set visual effects
|
|
for (int visualEffect = 0; visualEffect < mVisualEffects.size(); visualEffect++) {
|
|
if (newPolicy.mVisualEffects.get(visualEffect) != STATE_UNSET) {
|
|
result.mVisualEffects.set(visualEffect, newPolicy.mVisualEffects.get(visualEffect));
|
|
}
|
|
}
|
|
|
|
// set allowed channels
|
|
if (newPolicy.mAllowChannels != CHANNEL_POLICY_UNSET) {
|
|
result.mAllowChannels = newPolicy.mAllowChannels;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @hide
|
|
*/
|
|
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
|
|
final long token = proto.start(fieldId);
|
|
|
|
proto.write(ZenPolicyProto.REMINDERS, getPriorityCategoryReminders());
|
|
proto.write(ZenPolicyProto.EVENTS, getPriorityCategoryEvents());
|
|
proto.write(ZenPolicyProto.MESSAGES, getPriorityCategoryMessages());
|
|
proto.write(ZenPolicyProto.CALLS, getPriorityCategoryCalls());
|
|
proto.write(ZenPolicyProto.REPEAT_CALLERS, getPriorityCategoryRepeatCallers());
|
|
proto.write(ZenPolicyProto.ALARMS, getPriorityCategoryAlarms());
|
|
proto.write(ZenPolicyProto.MEDIA, getPriorityCategoryMedia());
|
|
proto.write(ZenPolicyProto.SYSTEM, getPriorityCategorySystem());
|
|
|
|
proto.write(ZenPolicyProto.FULL_SCREEN_INTENT, getVisualEffectFullScreenIntent());
|
|
proto.write(ZenPolicyProto.LIGHTS, getVisualEffectLights());
|
|
proto.write(ZenPolicyProto.PEEK, getVisualEffectPeek());
|
|
proto.write(ZenPolicyProto.STATUS_BAR, getVisualEffectStatusBar());
|
|
proto.write(ZenPolicyProto.BADGE, getVisualEffectBadge());
|
|
proto.write(ZenPolicyProto.AMBIENT, getVisualEffectAmbient());
|
|
proto.write(ZenPolicyProto.NOTIFICATION_LIST, getVisualEffectNotificationList());
|
|
|
|
proto.write(ZenPolicyProto.PRIORITY_MESSAGES, getPriorityMessageSenders());
|
|
proto.write(ZenPolicyProto.PRIORITY_CALLS, getPriorityCallSenders());
|
|
proto.end(token);
|
|
}
|
|
|
|
/**
|
|
* Converts a policy to a statsd proto.
|
|
* @hide
|
|
*/
|
|
public byte[] toProto() {
|
|
// TODO: b/308672510 - log user-customized ZenPolicy fields to DNDPolicyProto.
|
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
|
ProtoOutputStream proto = new ProtoOutputStream(bytes);
|
|
|
|
proto.write(DNDPolicyProto.CALLS, getPriorityCategoryCalls());
|
|
proto.write(DNDPolicyProto.REPEAT_CALLERS, getPriorityCategoryRepeatCallers());
|
|
proto.write(DNDPolicyProto.MESSAGES, getPriorityCategoryMessages());
|
|
proto.write(DNDPolicyProto.CONVERSATIONS, getPriorityCategoryConversations());
|
|
proto.write(DNDPolicyProto.REMINDERS, getPriorityCategoryReminders());
|
|
proto.write(DNDPolicyProto.EVENTS, getPriorityCategoryEvents());
|
|
proto.write(DNDPolicyProto.ALARMS, getPriorityCategoryAlarms());
|
|
proto.write(DNDPolicyProto.MEDIA, getPriorityCategoryMedia());
|
|
proto.write(DNDPolicyProto.SYSTEM, getPriorityCategorySystem());
|
|
|
|
proto.write(DNDPolicyProto.FULLSCREEN, getVisualEffectFullScreenIntent());
|
|
proto.write(DNDPolicyProto.LIGHTS, getVisualEffectLights());
|
|
proto.write(DNDPolicyProto.PEEK, getVisualEffectPeek());
|
|
proto.write(DNDPolicyProto.STATUS_BAR, getVisualEffectStatusBar());
|
|
proto.write(DNDPolicyProto.BADGE, getVisualEffectBadge());
|
|
proto.write(DNDPolicyProto.AMBIENT, getVisualEffectAmbient());
|
|
proto.write(DNDPolicyProto.NOTIFICATION_LIST, getVisualEffectNotificationList());
|
|
|
|
proto.write(DNDPolicyProto.ALLOW_CALLS_FROM, getPriorityCallSenders());
|
|
proto.write(DNDPolicyProto.ALLOW_MESSAGES_FROM, getPriorityMessageSenders());
|
|
proto.write(DNDPolicyProto.ALLOW_CONVERSATIONS_FROM, getPriorityConversationSenders());
|
|
|
|
if (Flags.modesApi()) {
|
|
proto.write(DNDPolicyProto.ALLOW_CHANNELS, getPriorityChannelsAllowed());
|
|
}
|
|
|
|
proto.flush();
|
|
return bytes.toByteArray();
|
|
}
|
|
|
|
/**
|
|
* Makes deep copy of this ZenPolicy.
|
|
* @hide
|
|
*/
|
|
public @NonNull ZenPolicy copy() {
|
|
final Parcel parcel = Parcel.obtain();
|
|
try {
|
|
writeToParcel(parcel, 0);
|
|
parcel.setDataPosition(0);
|
|
return CREATOR.createFromParcel(parcel);
|
|
} finally {
|
|
parcel.recycle();
|
|
}
|
|
}
|
|
}
|