summaryrefslogtreecommitdiff
path: root/core/java/android/app/NotificationChannel.java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android/app/NotificationChannel.java')
-rw-r--r--core/java/android/app/NotificationChannel.java302
1 files changed, 243 insertions, 59 deletions
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index b08f0354327c..cf2f7690bc2c 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -15,6 +15,7 @@
*/
package android.app;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -23,6 +24,7 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ShortcutInfo;
import android.media.AudioAttributes;
import android.net.Uri;
import android.os.Parcel;
@@ -57,6 +59,23 @@ public final class NotificationChannel implements Parcelable {
public static final String DEFAULT_CHANNEL_ID = "miscellaneous";
/**
+ * The formatter used by the system to create an id for notification
+ * channels when it automatically creates conversation channels on behalf of an app. The format
+ * string takes two arguments, in this order: the
+ * {@link #getId()} of the original notification channel, and the
+ * {@link ShortcutInfo#getId() id} of the conversation.
+ * @hide
+ */
+ public static final String CONVERSATION_CHANNEL_ID_FORMAT = "%1$s : %2$s";
+
+ /**
+ * TODO: STOPSHIP remove
+ * Conversation id to use for apps that aren't providing them yet.
+ * @hide
+ */
+ public static final String PLACEHOLDER_CONVERSATION_ID = ":placeholder_id";
+
+ /**
* The maximum length for text fields in a NotificationChannel. Fields will be truncated at this
* limit.
*/
@@ -83,7 +102,12 @@ public final class NotificationChannel implements Parcelable {
private static final String ATT_FG_SERVICE_SHOWN = "fgservice";
private static final String ATT_GROUP = "group";
private static final String ATT_BLOCKABLE_SYSTEM = "blockable_system";
- private static final String ATT_ALLOW_BUBBLE = "can_bubble";
+ private static final String ATT_ALLOW_BUBBLE = "allow_bubbles";
+ private static final String ATT_ORIG_IMP = "orig_imp";
+ private static final String ATT_PARENT_CHANNEL = "parent";
+ private static final String ATT_CONVERSATION_ID = "conv_id";
+ private static final String ATT_IMP_CONVERSATION = "imp_conv";
+ private static final String ATT_DEMOTE = "dem";
private static final String DELIMITER = ",";
/**
@@ -109,6 +133,8 @@ public final class NotificationChannel implements Parcelable {
/**
* @hide
*/
+ @SystemApi
+ @TestApi
public static final int USER_LOCKED_SOUND = 0x00000020;
/**
@@ -135,6 +161,19 @@ public final class NotificationChannel implements Parcelable {
USER_LOCKED_ALLOW_BUBBLE
};
+ /**
+ * @hide
+ */
+ public static final int DEFAULT_ALLOW_BUBBLE = -1;
+ /**
+ * @hide
+ */
+ public static final int ALLOW_BUBBLE_ON = 1;
+ /**
+ * @hide
+ */
+ public static final int ALLOW_BUBBLE_OFF = 0;
+
private static final int DEFAULT_LIGHT_COLOR = 0;
private static final int DEFAULT_VISIBILITY =
NotificationManager.VISIBILITY_NO_OVERRIDE;
@@ -142,13 +181,13 @@ public final class NotificationChannel implements Parcelable {
NotificationManager.IMPORTANCE_UNSPECIFIED;
private static final boolean DEFAULT_DELETED = false;
private static final boolean DEFAULT_SHOW_BADGE = true;
- private static final boolean DEFAULT_ALLOW_BUBBLE = true;
@UnsupportedAppUsage
- private final String mId;
+ private String mId;
private String mName;
private String mDesc;
private int mImportance = DEFAULT_IMPORTANCE;
+ private int mOriginalImportance = DEFAULT_IMPORTANCE;
private boolean mBypassDnd;
private int mLockscreenVisibility = DEFAULT_VISIBILITY;
private Uri mSound = Settings.System.DEFAULT_NOTIFICATION_URI;
@@ -166,9 +205,13 @@ public final class NotificationChannel implements Parcelable {
private AudioAttributes mAudioAttributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
// If this is a blockable system notification channel.
private boolean mBlockableSystem = false;
- private boolean mAllowBubbles = DEFAULT_ALLOW_BUBBLE;
+ private int mAllowBubbles = DEFAULT_ALLOW_BUBBLE;
private boolean mImportanceLockedByOEM;
private boolean mImportanceLockedDefaultApp;
+ private String mParentId = null;
+ private String mConversationId = null;
+ private boolean mDemoted = false;
+ private boolean mImportantConvo = false;
/**
* Creates a notification channel.
@@ -230,8 +273,13 @@ public final class NotificationChannel implements Parcelable {
mAudioAttributes = in.readInt() > 0 ? AudioAttributes.CREATOR.createFromParcel(in) : null;
mLightColor = in.readInt();
mBlockableSystem = in.readBoolean();
- mAllowBubbles = in.readBoolean();
+ mAllowBubbles = in.readInt();
mImportanceLockedByOEM = in.readBoolean();
+ mOriginalImportance = in.readInt();
+ mParentId = in.readString();
+ mConversationId = in.readString();
+ mDemoted = in.readBoolean();
+ mImportantConvo = in.readBoolean();
}
@Override
@@ -284,13 +332,19 @@ public final class NotificationChannel implements Parcelable {
}
dest.writeInt(mLightColor);
dest.writeBoolean(mBlockableSystem);
- dest.writeBoolean(mAllowBubbles);
+ dest.writeInt(mAllowBubbles);
dest.writeBoolean(mImportanceLockedByOEM);
+ dest.writeInt(mOriginalImportance);
+ dest.writeString(mParentId);
+ dest.writeString(mConversationId);
+ dest.writeBoolean(mDemoted);
+ dest.writeBoolean(mImportantConvo);
}
/**
* @hide
*/
+ @TestApi
public void lockFields(int field) {
mUserLockedFields |= field;
}
@@ -305,6 +359,7 @@ public final class NotificationChannel implements Parcelable {
/**
* @hide
*/
+ @TestApi
public void setFgServiceShown(boolean shown) {
mFgServiceShown = shown;
}
@@ -312,21 +367,33 @@ public final class NotificationChannel implements Parcelable {
/**
* @hide
*/
+ @TestApi
public void setDeleted(boolean deleted) {
mDeleted = deleted;
}
/**
+ * @hide
+ */
+ @TestApi
+ public void setImportantConversation(boolean importantConvo) {
+ mImportantConvo = importantConvo;
+ }
+
+ /**
* Allows users to block notifications sent through this channel, if this channel belongs to
- * a package that is signed with the system signature. If the channel does not belong to a
- * package that is signed with the system signature, this method does nothing.
- * @param blockableSystem if {@code true}, allows users to block notifications on this channel.
+ * a package that is signed with the system signature.
+ *
+ * If the channel does not belong to a package that is signed with the system signature, this
+ * method does nothing, since such channels are blockable by default and cannot be set to be
+ * unblockable.
+ * @param blockable if {@code true}, allows users to block notifications on this channel.
* @hide
*/
@SystemApi
@TestApi
- public void setBlockableSystem(boolean blockableSystem) {
- mBlockableSystem = blockableSystem;
+ public void setBlockable(boolean blockable) {
+ mBlockableSystem = blockable;
}
// Modifiable by apps post channel creation
@@ -357,6 +424,13 @@ public final class NotificationChannel implements Parcelable {
return input;
}
+ /**
+ * @hide
+ */
+ public void setId(String id) {
+ mId = id;
+ }
+
// Modifiable by apps on channel creation.
/**
@@ -483,19 +557,36 @@ public final class NotificationChannel implements Parcelable {
}
/**
- * Sets whether notifications posted to this channel can appear outside of the notification
- * shade, floating over other apps' content as a bubble.
- *
- * <p>This value will be ignored for channels that aren't allowed to pop on screen (that is,
- * channels whose {@link #getImportance() importance} is <
- * {@link NotificationManager#IMPORTANCE_HIGH}.</p>
- *
- * <p>Only modifiable before the channel is submitted to
- * * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.</p>
+ * As of Android 11 this value is no longer respected.
+ * @see #canBubble()
* @see Notification#getBubbleMetadata()
*/
public void setAllowBubbles(boolean allowBubbles) {
- mAllowBubbles = allowBubbles;
+ mAllowBubbles = allowBubbles ? ALLOW_BUBBLE_ON : ALLOW_BUBBLE_OFF;
+ }
+
+ /**
+ * @hide
+ */
+ public void setAllowBubbles(int allowed) {
+ mAllowBubbles = allowed;
+ }
+
+ /**
+ * Sets this channel as being converastion-centric. Different settings and functionality may be
+ * exposed for conversation-centric channels.
+ *
+ * @param parentChannelId The {@link #getId()} id} of the generic channel that notifications of
+ * this type would be posted to in absence of a specific conversation id.
+ * For example, if this channel represents 'Messages from Person A', the
+ * parent channel would be 'Messages.'
+ * @param conversationId The {@link ShortcutInfo#getId()} of the shortcut representing this
+ * channel's conversation.
+ */
+ public void setConversationId(@NonNull String parentChannelId,
+ @NonNull String conversationId) {
+ mParentId = parentChannelId;
+ mConversationId = conversationId;
}
/**
@@ -540,6 +631,18 @@ public final class NotificationChannel implements Parcelable {
}
/**
+ * Whether or not notifications in this conversation are considered important.
+ *
+ * <p>Important conversations may get special visual treatment, and might be able to bypass DND.
+ *
+ * <p>This is only valid for channels that represent conversations, that is, those with a valid
+ * {@link #getConversationId() conversation id}.
+ */
+ public boolean isImportantConversation() {
+ return mImportantConvo;
+ }
+
+ /**
* Returns the notification sound for this channel.
*/
public Uri getSound() {
@@ -611,14 +714,39 @@ public final class NotificationChannel implements Parcelable {
}
/**
- * Returns whether notifications posted to this channel can display outside of the notification
- * shade, in a floating window on top of other apps.
+ * Returns whether notifications posted to this channel are allowed to display outside of the
+ * notification shade, in a floating window on top of other apps.
+ *
+ * @see Notification#getBubbleMetadata()
*/
public boolean canBubble() {
+ return mAllowBubbles == ALLOW_BUBBLE_ON;
+ }
+
+ /**
+ * @hide
+ */
+ public int getAllowBubbles() {
return mAllowBubbles;
}
/**
+ * Returns the {@link #getId() id} of the parent notification channel to this channel, if it's
+ * a conversation related channel. See {@link #setConversationId(String, String)}.
+ */
+ public @Nullable String getParentChannelId() {
+ return mParentId;
+ }
+
+ /**
+ * Returns the {@link ShortcutInfo#getId() id} of the conversation backing this channel, if it's
+ * associated with a conversation. See {@link #setConversationId(String, String)}.
+ */
+ public @Nullable String getConversationId() {
+ return mConversationId;
+ }
+
+ /**
* @hide
*/
@SystemApi
@@ -645,7 +773,7 @@ public final class NotificationChannel implements Parcelable {
* @hide
*/
@TestApi
- public boolean isBlockableSystem() {
+ public boolean isBlockable() {
return mBlockableSystem;
}
@@ -682,6 +810,36 @@ public final class NotificationChannel implements Parcelable {
}
/**
+ * @hide
+ */
+ @TestApi
+ public int getOriginalImportance() {
+ return mOriginalImportance;
+ }
+
+ /**
+ * @hide
+ */
+ @TestApi
+ public void setOriginalImportance(int importance) {
+ mOriginalImportance = importance;
+ }
+
+ /**
+ * @hide
+ */
+ public void setDemoted(boolean demoted) {
+ mDemoted = demoted;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean isDemoted() {
+ return mDemoted;
+ }
+
+ /**
* Returns whether the user has chosen the importance of this channel, either to affirm the
* initial selection from the app, or changed it to be higher or lower.
* @see #getImportance()
@@ -691,6 +849,14 @@ public final class NotificationChannel implements Parcelable {
}
/**
+ * Returns whether the user has chosen the sound of this channel.
+ * @see #getSound()
+ */
+ public boolean hasUserSetSound() {
+ return (mUserLockedFields & USER_LOCKED_SOUND) != 0;
+ }
+
+ /**
* @hide
*/
public void populateFromXmlForRestore(XmlPullParser parser, Context context) {
@@ -731,8 +897,13 @@ public final class NotificationChannel implements Parcelable {
setGroup(parser.getAttributeValue(null, ATT_GROUP));
lockFields(safeInt(parser, ATT_USER_LOCKED, 0));
setFgServiceShown(safeBool(parser, ATT_FG_SERVICE_SHOWN, false));
- setBlockableSystem(safeBool(parser, ATT_BLOCKABLE_SYSTEM, false));
- setAllowBubbles(safeBool(parser, ATT_ALLOW_BUBBLE, DEFAULT_ALLOW_BUBBLE));
+ setBlockable(safeBool(parser, ATT_BLOCKABLE_SYSTEM, false));
+ setAllowBubbles(safeInt(parser, ATT_ALLOW_BUBBLE, DEFAULT_ALLOW_BUBBLE));
+ setOriginalImportance(safeInt(parser, ATT_ORIG_IMP, DEFAULT_IMPORTANCE));
+ setConversationId(parser.getAttributeValue(null, ATT_PARENT_CHANNEL),
+ parser.getAttributeValue(null, ATT_CONVERSATION_ID));
+ setDemoted(safeBool(parser, ATT_DEMOTE, false));
+ setImportantConversation(safeBool(parser, ATT_IMP_CONVERSATION, false));
}
@Nullable
@@ -848,11 +1019,26 @@ public final class NotificationChannel implements Parcelable {
if (getGroup() != null) {
out.attribute(null, ATT_GROUP, getGroup());
}
- if (isBlockableSystem()) {
- out.attribute(null, ATT_BLOCKABLE_SYSTEM, Boolean.toString(isBlockableSystem()));
+ if (isBlockable()) {
+ out.attribute(null, ATT_BLOCKABLE_SYSTEM, Boolean.toString(isBlockable()));
+ }
+ if (getAllowBubbles() != DEFAULT_ALLOW_BUBBLE) {
+ out.attribute(null, ATT_ALLOW_BUBBLE, Integer.toString(getAllowBubbles()));
+ }
+ if (getOriginalImportance() != DEFAULT_IMPORTANCE) {
+ out.attribute(null, ATT_ORIG_IMP, Integer.toString(getOriginalImportance()));
+ }
+ if (getParentChannelId() != null) {
+ out.attribute(null, ATT_PARENT_CHANNEL, getParentChannelId());
+ }
+ if (getConversationId() != null) {
+ out.attribute(null, ATT_CONVERSATION_ID, getConversationId());
+ }
+ if (isDemoted()) {
+ out.attribute(null, ATT_DEMOTE, Boolean.toString(isDemoted()));
}
- if (canBubble() != DEFAULT_ALLOW_BUBBLE) {
- out.attribute(null, ATT_ALLOW_BUBBLE, Boolean.toString(canBubble()));
+ if (isImportantConversation()) {
+ out.attribute(null, ATT_IMP_CONVERSATION, Boolean.toString(isImportantConversation()));
}
// mImportanceLockedDefaultApp and mImportanceLockedByOEM have a different source of
@@ -898,8 +1084,9 @@ public final class NotificationChannel implements Parcelable {
record.put(ATT_SHOW_BADGE, Boolean.toString(canShowBadge()));
record.put(ATT_DELETED, Boolean.toString(isDeleted()));
record.put(ATT_GROUP, getGroup());
- record.put(ATT_BLOCKABLE_SYSTEM, isBlockableSystem());
- record.put(ATT_ALLOW_BUBBLE, canBubble());
+ record.put(ATT_BLOCKABLE_SYSTEM, isBlockable());
+ record.put(ATT_ALLOW_BUBBLE, getAllowBubbles());
+ // TODO: original importance
return record;
}
@@ -999,7 +1186,7 @@ public final class NotificationChannel implements Parcelable {
&& mVibrationEnabled == that.mVibrationEnabled
&& mShowBadge == that.mShowBadge
&& isDeleted() == that.isDeleted()
- && isBlockableSystem() == that.isBlockableSystem()
+ && isBlockable() == that.isBlockable()
&& mAllowBubbles == that.mAllowBubbles
&& Objects.equals(getId(), that.getId())
&& Objects.equals(getName(), that.getName())
@@ -1009,7 +1196,12 @@ public final class NotificationChannel implements Parcelable {
&& Objects.equals(getGroup(), that.getGroup())
&& Objects.equals(getAudioAttributes(), that.getAudioAttributes())
&& mImportanceLockedByOEM == that.mImportanceLockedByOEM
- && mImportanceLockedDefaultApp == that.mImportanceLockedDefaultApp;
+ && mImportanceLockedDefaultApp == that.mImportanceLockedDefaultApp
+ && mOriginalImportance == that.mOriginalImportance
+ && Objects.equals(getParentChannelId(), that.getParentChannelId())
+ && Objects.equals(getConversationId(), that.getConversationId())
+ && isDemoted() == that.isDemoted()
+ && isImportantConversation() == that.isImportantConversation();
}
@Override
@@ -1018,8 +1210,9 @@ public final class NotificationChannel implements Parcelable {
getLockscreenVisibility(), getSound(), mLights, getLightColor(),
getUserLockedFields(),
isFgServiceShown(), mVibrationEnabled, mShowBadge, isDeleted(), getGroup(),
- getAudioAttributes(), isBlockableSystem(), mAllowBubbles,
- mImportanceLockedByOEM, mImportanceLockedDefaultApp);
+ getAudioAttributes(), isBlockable(), mAllowBubbles,
+ mImportanceLockedByOEM, mImportanceLockedDefaultApp, mOriginalImportance,
+ mParentId, mConversationId, mDemoted, mImportantConvo);
result = 31 * result + Arrays.hashCode(mVibration);
return result;
}
@@ -1030,25 +1223,7 @@ public final class NotificationChannel implements Parcelable {
String output = "NotificationChannel{"
+ "mId='" + mId + '\''
+ ", mName=" + redactedName
- + ", mDescription=" + (!TextUtils.isEmpty(mDesc) ? "hasDescription " : "")
- + ", mImportance=" + mImportance
- + ", mBypassDnd=" + mBypassDnd
- + ", mLockscreenVisibility=" + mLockscreenVisibility
- + ", mSound=" + mSound
- + ", mLights=" + mLights
- + ", mLightColor=" + mLightColor
- + ", mVibration=" + Arrays.toString(mVibration)
- + ", mUserLockedFields=" + Integer.toHexString(mUserLockedFields)
- + ", mFgServiceShown=" + mFgServiceShown
- + ", mVibrationEnabled=" + mVibrationEnabled
- + ", mShowBadge=" + mShowBadge
- + ", mDeleted=" + mDeleted
- + ", mGroup='" + mGroup + '\''
- + ", mAudioAttributes=" + mAudioAttributes
- + ", mBlockableSystem=" + mBlockableSystem
- + ", mAllowBubbles=" + mAllowBubbles
- + ", mImportanceLockedByOEM=" + mImportanceLockedByOEM
- + ", mImportanceLockedDefaultApp=" + mImportanceLockedDefaultApp
+ + getFieldsString()
+ '}';
pw.println(prefix + output);
}
@@ -1058,7 +1233,12 @@ public final class NotificationChannel implements Parcelable {
return "NotificationChannel{"
+ "mId='" + mId + '\''
+ ", mName=" + mName
- + ", mDescription=" + (!TextUtils.isEmpty(mDesc) ? "hasDescription " : "")
+ + getFieldsString()
+ + '}';
+ }
+
+ private String getFieldsString() {
+ return ", mDescription=" + (!TextUtils.isEmpty(mDesc) ? "hasDescription " : "")
+ ", mImportance=" + mImportance
+ ", mBypassDnd=" + mBypassDnd
+ ", mLockscreenVisibility=" + mLockscreenVisibility
@@ -1077,11 +1257,15 @@ public final class NotificationChannel implements Parcelable {
+ ", mAllowBubbles=" + mAllowBubbles
+ ", mImportanceLockedByOEM=" + mImportanceLockedByOEM
+ ", mImportanceLockedDefaultApp=" + mImportanceLockedDefaultApp
- + '}';
+ + ", mOriginalImp=" + mOriginalImportance
+ + ", mParent=" + mParentId
+ + ", mConversationId=" + mConversationId
+ + ", mDemoted=" + mDemoted
+ + ", mImportantConvo=" + mImportantConvo;
}
/** @hide */
- public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ public void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(NotificationChannelProto.ID, mId);
@@ -1107,7 +1291,7 @@ public final class NotificationChannel implements Parcelable {
proto.write(NotificationChannelProto.IS_DELETED, mDeleted);
proto.write(NotificationChannelProto.GROUP, mGroup);
if (mAudioAttributes != null) {
- mAudioAttributes.writeToProto(proto, NotificationChannelProto.AUDIO_ATTRIBUTES);
+ mAudioAttributes.dumpDebug(proto, NotificationChannelProto.AUDIO_ATTRIBUTES);
}
proto.write(NotificationChannelProto.IS_BLOCKABLE_SYSTEM, mBlockableSystem);
proto.write(NotificationChannelProto.ALLOW_APP_OVERLAY, mAllowBubbles);