| /* |
| * Copyright (C) 2017 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.app; |
| |
| import android.annotation.SystemApi; |
| import android.annotation.TestApi; |
| import android.compat.annotation.UnsupportedAppUsage; |
| import android.content.Intent; |
| import android.os.Parcel; |
| import android.os.Parcelable; |
| import android.text.TextUtils; |
| import android.util.proto.ProtoOutputStream; |
| |
| import org.json.JSONException; |
| import org.json.JSONObject; |
| import org.xmlpull.v1.XmlPullParser; |
| import org.xmlpull.v1.XmlSerializer; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Objects; |
| |
| /** |
| * A grouping of related notification channels. e.g., channels that all belong to a single account. |
| */ |
| public final class NotificationChannelGroup implements Parcelable { |
| |
| /** |
| * The maximum length for text fields in a NotificationChannelGroup. Fields will be truncated at |
| * this limit. |
| */ |
| private static final int MAX_TEXT_LENGTH = 1000; |
| |
| private static final String TAG_GROUP = "channelGroup"; |
| private static final String ATT_NAME = "name"; |
| private static final String ATT_DESC = "desc"; |
| private static final String ATT_ID = "id"; |
| private static final String ATT_BLOCKED = "blocked"; |
| private static final String ATT_USER_LOCKED = "locked"; |
| |
| /** |
| * @hide |
| */ |
| public static final int USER_LOCKED_BLOCKED_STATE = 0x00000001; |
| |
| /** |
| * @see #getId() |
| */ |
| @UnsupportedAppUsage |
| private final String mId; |
| private CharSequence mName; |
| private String mDescription; |
| private boolean mBlocked; |
| private List<NotificationChannel> mChannels = new ArrayList<>(); |
| // Bitwise representation of fields that have been changed by the user |
| private int mUserLockedFields; |
| |
| /** |
| * Creates a notification channel group. |
| * |
| * @param id The id of the group. Must be unique per package. the value may be truncated if |
| * it is too long. |
| * @param name The user visible name of the group. You can rename this group when the system |
| * locale changes by listening for the {@link Intent#ACTION_LOCALE_CHANGED} |
| * broadcast. <p>The recommended maximum length is 40 characters; the value may be |
| * truncated if it is too long. |
| */ |
| public NotificationChannelGroup(String id, CharSequence name) { |
| this.mId = getTrimmedString(id); |
| this.mName = name != null ? getTrimmedString(name.toString()) : null; |
| } |
| |
| /** |
| * @hide |
| */ |
| protected NotificationChannelGroup(Parcel in) { |
| if (in.readByte() != 0) { |
| mId = in.readString(); |
| } else { |
| mId = null; |
| } |
| mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); |
| if (in.readByte() != 0) { |
| mDescription = in.readString(); |
| } else { |
| mDescription = null; |
| } |
| in.readParcelableList(mChannels, NotificationChannel.class.getClassLoader()); |
| mBlocked = in.readBoolean(); |
| mUserLockedFields = in.readInt(); |
| } |
| |
| private String getTrimmedString(String input) { |
| if (input != null && input.length() > MAX_TEXT_LENGTH) { |
| return input.substring(0, MAX_TEXT_LENGTH); |
| } |
| return input; |
| } |
| |
| @Override |
| public void writeToParcel(Parcel dest, int flags) { |
| if (mId != null) { |
| dest.writeByte((byte) 1); |
| dest.writeString(mId); |
| } else { |
| dest.writeByte((byte) 0); |
| } |
| TextUtils.writeToParcel(mName, dest, flags); |
| if (mDescription != null) { |
| dest.writeByte((byte) 1); |
| dest.writeString(mDescription); |
| } else { |
| dest.writeByte((byte) 0); |
| } |
| dest.writeParcelableList(mChannels, flags); |
| dest.writeBoolean(mBlocked); |
| dest.writeInt(mUserLockedFields); |
| } |
| |
| /** |
| * Returns the id of this group. |
| */ |
| public String getId() { |
| return mId; |
| } |
| |
| /** |
| * Returns the user visible name of this group. |
| */ |
| public CharSequence getName() { |
| return mName; |
| } |
| |
| /** |
| * Returns the user visible description of this group. |
| */ |
| public String getDescription() { |
| return mDescription; |
| } |
| |
| /** |
| * Returns the list of channels that belong to this group |
| */ |
| public List<NotificationChannel> getChannels() { |
| return mChannels; |
| } |
| |
| /** |
| * Returns whether or not notifications posted to {@link NotificationChannel channels} belonging |
| * to this group are blocked. This value is independent of |
| * {@link NotificationManager#areNotificationsEnabled()} and |
| * {@link NotificationChannel#getImportance()}. |
| */ |
| public boolean isBlocked() { |
| return mBlocked; |
| } |
| |
| /** |
| * Sets the user visible description of this group. |
| * |
| * <p>The recommended maximum length is 300 characters; the value may be truncated if it is too |
| * long. |
| */ |
| public void setDescription(String description) { |
| mDescription = getTrimmedString(description); |
| } |
| |
| /** |
| * @hide |
| */ |
| @TestApi |
| public void setBlocked(boolean blocked) { |
| mBlocked = blocked; |
| } |
| |
| /** |
| * @hide |
| */ |
| public void addChannel(NotificationChannel channel) { |
| mChannels.add(channel); |
| } |
| |
| /** |
| * @hide |
| */ |
| public void setChannels(List<NotificationChannel> channels) { |
| mChannels = channels; |
| } |
| |
| /** |
| * @hide |
| */ |
| @TestApi |
| public void lockFields(int field) { |
| mUserLockedFields |= field; |
| } |
| |
| /** |
| * @hide |
| */ |
| public void unlockFields(int field) { |
| mUserLockedFields &= ~field; |
| } |
| |
| /** |
| * @hide |
| */ |
| @TestApi |
| public int getUserLockedFields() { |
| return mUserLockedFields; |
| } |
| |
| /** |
| * @hide |
| */ |
| public void populateFromXml(XmlPullParser parser) { |
| // Name, id, and importance are set in the constructor. |
| setDescription(parser.getAttributeValue(null, ATT_DESC)); |
| setBlocked(safeBool(parser, ATT_BLOCKED, false)); |
| } |
| |
| private static boolean safeBool(XmlPullParser parser, String att, boolean defValue) { |
| final String value = parser.getAttributeValue(null, att); |
| if (TextUtils.isEmpty(value)) return defValue; |
| return Boolean.parseBoolean(value); |
| } |
| |
| /** |
| * @hide |
| */ |
| public void writeXml(XmlSerializer out) throws IOException { |
| out.startTag(null, TAG_GROUP); |
| |
| out.attribute(null, ATT_ID, getId()); |
| if (getName() != null) { |
| out.attribute(null, ATT_NAME, getName().toString()); |
| } |
| if (getDescription() != null) { |
| out.attribute(null, ATT_DESC, getDescription().toString()); |
| } |
| out.attribute(null, ATT_BLOCKED, Boolean.toString(isBlocked())); |
| out.attribute(null, ATT_USER_LOCKED, Integer.toString(mUserLockedFields)); |
| |
| out.endTag(null, TAG_GROUP); |
| } |
| |
| /** |
| * @hide |
| */ |
| @SystemApi |
| public JSONObject toJson() throws JSONException { |
| JSONObject record = new JSONObject(); |
| record.put(ATT_ID, getId()); |
| record.put(ATT_NAME, getName()); |
| record.put(ATT_DESC, getDescription()); |
| record.put(ATT_BLOCKED, isBlocked()); |
| record.put(ATT_USER_LOCKED, mUserLockedFields); |
| return record; |
| } |
| |
| public static final @android.annotation.NonNull Creator<NotificationChannelGroup> CREATOR = |
| new Creator<NotificationChannelGroup>() { |
| @Override |
| public NotificationChannelGroup createFromParcel(Parcel in) { |
| return new NotificationChannelGroup(in); |
| } |
| |
| @Override |
| public NotificationChannelGroup[] newArray(int size) { |
| return new NotificationChannelGroup[size]; |
| } |
| }; |
| |
| @Override |
| public int describeContents() { |
| return 0; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) return true; |
| if (o == null || getClass() != o.getClass()) return false; |
| NotificationChannelGroup that = (NotificationChannelGroup) o; |
| return isBlocked() == that.isBlocked() && |
| mUserLockedFields == that.mUserLockedFields && |
| Objects.equals(getId(), that.getId()) && |
| Objects.equals(getName(), that.getName()) && |
| Objects.equals(getDescription(), that.getDescription()) && |
| Objects.equals(getChannels(), that.getChannels()); |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(getId(), getName(), getDescription(), isBlocked(), getChannels(), |
| mUserLockedFields); |
| } |
| |
| @Override |
| public NotificationChannelGroup clone() { |
| NotificationChannelGroup cloned = new NotificationChannelGroup(getId(), getName()); |
| cloned.setDescription(getDescription()); |
| cloned.setBlocked(isBlocked()); |
| cloned.setChannels(getChannels()); |
| cloned.lockFields(mUserLockedFields); |
| return cloned; |
| } |
| |
| @Override |
| public String toString() { |
| return "NotificationChannelGroup{" |
| + "mId='" + mId + '\'' |
| + ", mName=" + mName |
| + ", mDescription=" + (!TextUtils.isEmpty(mDescription) ? "hasDescription " : "") |
| + ", mBlocked=" + mBlocked |
| + ", mChannels=" + mChannels |
| + ", mUserLockedFields=" + mUserLockedFields |
| + '}'; |
| } |
| |
| /** @hide */ |
| public void dumpDebug(ProtoOutputStream proto, long fieldId) { |
| final long token = proto.start(fieldId); |
| |
| proto.write(NotificationChannelGroupProto.ID, mId); |
| proto.write(NotificationChannelGroupProto.NAME, mName.toString()); |
| proto.write(NotificationChannelGroupProto.DESCRIPTION, mDescription); |
| proto.write(NotificationChannelGroupProto.IS_BLOCKED, mBlocked); |
| for (NotificationChannel channel : mChannels) { |
| channel.dumpDebug(proto, NotificationChannelGroupProto.CHANNELS); |
| } |
| proto.end(token); |
| } |
| } |