Home | History | Annotate | Download | only in app
      1 /*
      2  * Copyright (C) 2017 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 package android.app;
     17 
     18 import android.annotation.SystemApi;
     19 import android.annotation.TestApi;
     20 import android.annotation.UnsupportedAppUsage;
     21 import android.content.Intent;
     22 import android.os.Parcel;
     23 import android.os.Parcelable;
     24 import android.text.TextUtils;
     25 import android.util.proto.ProtoOutputStream;
     26 
     27 import org.json.JSONException;
     28 import org.json.JSONObject;
     29 import org.xmlpull.v1.XmlPullParser;
     30 import org.xmlpull.v1.XmlSerializer;
     31 
     32 import java.io.IOException;
     33 import java.util.ArrayList;
     34 import java.util.List;
     35 import java.util.Objects;
     36 
     37 /**
     38  * A grouping of related notification channels. e.g., channels that all belong to a single account.
     39  */
     40 public final class NotificationChannelGroup implements Parcelable {
     41 
     42     /**
     43      * The maximum length for text fields in a NotificationChannelGroup. Fields will be truncated at
     44      * this limit.
     45      */
     46     private static final int MAX_TEXT_LENGTH = 1000;
     47 
     48     private static final String TAG_GROUP = "channelGroup";
     49     private static final String ATT_NAME = "name";
     50     private static final String ATT_DESC = "desc";
     51     private static final String ATT_ID = "id";
     52     private static final String ATT_BLOCKED = "blocked";
     53     private static final String ATT_USER_LOCKED = "locked";
     54 
     55     /**
     56      * @hide
     57      */
     58     public static final int USER_LOCKED_BLOCKED_STATE = 0x00000001;
     59 
     60     /**
     61      * @see #getId()
     62      */
     63     @UnsupportedAppUsage
     64     private final String mId;
     65     private CharSequence mName;
     66     private String mDescription;
     67     private boolean mBlocked;
     68     private List<NotificationChannel> mChannels = new ArrayList<>();
     69     // Bitwise representation of fields that have been changed by the user
     70     private int mUserLockedFields;
     71 
     72     /**
     73      * Creates a notification channel group.
     74      *
     75      * @param id The id of the group. Must be unique per package.  the value may be truncated if
     76      *           it is too long.
     77      * @param name The user visible name of the group. You can rename this group when the system
     78      *             locale changes by listening for the {@link Intent#ACTION_LOCALE_CHANGED}
     79      *             broadcast. <p>The recommended maximum length is 40 characters; the value may be
     80      *             truncated if it is too long.
     81      */
     82     public NotificationChannelGroup(String id, CharSequence name) {
     83         this.mId = getTrimmedString(id);
     84         this.mName = name != null ? getTrimmedString(name.toString()) : null;
     85     }
     86 
     87     /**
     88      * @hide
     89      */
     90     protected NotificationChannelGroup(Parcel in) {
     91         if (in.readByte() != 0) {
     92             mId = in.readString();
     93         } else {
     94             mId = null;
     95         }
     96         mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
     97         if (in.readByte() != 0) {
     98             mDescription = in.readString();
     99         } else {
    100             mDescription = null;
    101         }
    102         in.readParcelableList(mChannels, NotificationChannel.class.getClassLoader());
    103         mBlocked = in.readBoolean();
    104         mUserLockedFields = in.readInt();
    105     }
    106 
    107     private String getTrimmedString(String input) {
    108         if (input != null && input.length() > MAX_TEXT_LENGTH) {
    109             return input.substring(0, MAX_TEXT_LENGTH);
    110         }
    111         return input;
    112     }
    113 
    114     @Override
    115     public void writeToParcel(Parcel dest, int flags) {
    116         if (mId != null) {
    117             dest.writeByte((byte) 1);
    118             dest.writeString(mId);
    119         } else {
    120             dest.writeByte((byte) 0);
    121         }
    122         TextUtils.writeToParcel(mName, dest, flags);
    123         if (mDescription != null) {
    124             dest.writeByte((byte) 1);
    125             dest.writeString(mDescription);
    126         } else {
    127             dest.writeByte((byte) 0);
    128         }
    129         dest.writeParcelableList(mChannels, flags);
    130         dest.writeBoolean(mBlocked);
    131         dest.writeInt(mUserLockedFields);
    132     }
    133 
    134     /**
    135      * Returns the id of this group.
    136      */
    137     public String getId() {
    138         return mId;
    139     }
    140 
    141     /**
    142      * Returns the user visible name of this group.
    143      */
    144     public CharSequence getName() {
    145         return mName;
    146     }
    147 
    148     /**
    149      * Returns the user visible description of this group.
    150      */
    151     public String getDescription() {
    152         return mDescription;
    153     }
    154 
    155     /**
    156      * Returns the list of channels that belong to this group
    157      */
    158     public List<NotificationChannel> getChannels() {
    159         return mChannels;
    160     }
    161 
    162     /**
    163      * Returns whether or not notifications posted to {@link NotificationChannel channels} belonging
    164      * to this group are blocked. This value is independent of
    165      * {@link NotificationManager#areNotificationsEnabled()} and
    166      * {@link NotificationChannel#getImportance()}.
    167      */
    168     public boolean isBlocked() {
    169         return mBlocked;
    170     }
    171 
    172     /**
    173      * Sets the user visible description of this group.
    174      *
    175      * <p>The recommended maximum length is 300 characters; the value may be truncated if it is too
    176      * long.
    177      */
    178     public void setDescription(String description) {
    179         mDescription = getTrimmedString(description);
    180     }
    181 
    182     /**
    183      * @hide
    184      */
    185     @TestApi
    186     public void setBlocked(boolean blocked) {
    187         mBlocked = blocked;
    188     }
    189 
    190     /**
    191      * @hide
    192      */
    193     public void addChannel(NotificationChannel channel) {
    194         mChannels.add(channel);
    195     }
    196 
    197     /**
    198      * @hide
    199      */
    200     public void setChannels(List<NotificationChannel> channels) {
    201         mChannels = channels;
    202     }
    203 
    204     /**
    205      * @hide
    206      */
    207     @TestApi
    208     public void lockFields(int field) {
    209         mUserLockedFields |= field;
    210     }
    211 
    212     /**
    213      * @hide
    214      */
    215     public void unlockFields(int field) {
    216         mUserLockedFields &= ~field;
    217     }
    218 
    219     /**
    220      * @hide
    221      */
    222     @TestApi
    223     public int getUserLockedFields() {
    224         return mUserLockedFields;
    225     }
    226 
    227     /**
    228      * @hide
    229      */
    230     public void populateFromXml(XmlPullParser parser) {
    231         // Name, id, and importance are set in the constructor.
    232         setDescription(parser.getAttributeValue(null, ATT_DESC));
    233         setBlocked(safeBool(parser, ATT_BLOCKED, false));
    234     }
    235 
    236     private static boolean safeBool(XmlPullParser parser, String att, boolean defValue) {
    237         final String value = parser.getAttributeValue(null, att);
    238         if (TextUtils.isEmpty(value)) return defValue;
    239         return Boolean.parseBoolean(value);
    240     }
    241 
    242     /**
    243      * @hide
    244      */
    245     public void writeXml(XmlSerializer out) throws IOException {
    246         out.startTag(null, TAG_GROUP);
    247 
    248         out.attribute(null, ATT_ID, getId());
    249         if (getName() != null) {
    250             out.attribute(null, ATT_NAME, getName().toString());
    251         }
    252         if (getDescription() != null) {
    253             out.attribute(null, ATT_DESC, getDescription().toString());
    254         }
    255         out.attribute(null, ATT_BLOCKED, Boolean.toString(isBlocked()));
    256         out.attribute(null, ATT_USER_LOCKED, Integer.toString(mUserLockedFields));
    257 
    258         out.endTag(null, TAG_GROUP);
    259     }
    260 
    261     /**
    262      * @hide
    263      */
    264     @SystemApi
    265     public JSONObject toJson() throws JSONException {
    266         JSONObject record = new JSONObject();
    267         record.put(ATT_ID, getId());
    268         record.put(ATT_NAME, getName());
    269         record.put(ATT_DESC, getDescription());
    270         record.put(ATT_BLOCKED, isBlocked());
    271         record.put(ATT_USER_LOCKED, mUserLockedFields);
    272         return record;
    273     }
    274 
    275     public static final @android.annotation.NonNull Creator<NotificationChannelGroup> CREATOR =
    276             new Creator<NotificationChannelGroup>() {
    277         @Override
    278         public NotificationChannelGroup createFromParcel(Parcel in) {
    279             return new NotificationChannelGroup(in);
    280         }
    281 
    282         @Override
    283         public NotificationChannelGroup[] newArray(int size) {
    284             return new NotificationChannelGroup[size];
    285         }
    286     };
    287 
    288     @Override
    289     public int describeContents() {
    290         return 0;
    291     }
    292 
    293     @Override
    294     public boolean equals(Object o) {
    295         if (this == o) return true;
    296         if (o == null || getClass() != o.getClass()) return false;
    297         NotificationChannelGroup that = (NotificationChannelGroup) o;
    298         return isBlocked() == that.isBlocked() &&
    299                 mUserLockedFields == that.mUserLockedFields &&
    300                 Objects.equals(getId(), that.getId()) &&
    301                 Objects.equals(getName(), that.getName()) &&
    302                 Objects.equals(getDescription(), that.getDescription()) &&
    303                 Objects.equals(getChannels(), that.getChannels());
    304     }
    305 
    306     @Override
    307     public int hashCode() {
    308         return Objects.hash(getId(), getName(), getDescription(), isBlocked(), getChannels(),
    309                 mUserLockedFields);
    310     }
    311 
    312     @Override
    313     public NotificationChannelGroup clone() {
    314         NotificationChannelGroup cloned = new NotificationChannelGroup(getId(), getName());
    315         cloned.setDescription(getDescription());
    316         cloned.setBlocked(isBlocked());
    317         cloned.setChannels(getChannels());
    318         cloned.lockFields(mUserLockedFields);
    319         return cloned;
    320     }
    321 
    322     @Override
    323     public String toString() {
    324         return "NotificationChannelGroup{"
    325                 + "mId='" + mId + '\''
    326                 + ", mName=" + mName
    327                 + ", mDescription=" + (!TextUtils.isEmpty(mDescription) ? "hasDescription " : "")
    328                 + ", mBlocked=" + mBlocked
    329                 + ", mChannels=" + mChannels
    330                 + ", mUserLockedFields=" + mUserLockedFields
    331                 + '}';
    332     }
    333 
    334     /** @hide */
    335     public void writeToProto(ProtoOutputStream proto, long fieldId) {
    336         final long token = proto.start(fieldId);
    337 
    338         proto.write(NotificationChannelGroupProto.ID, mId);
    339         proto.write(NotificationChannelGroupProto.NAME, mName.toString());
    340         proto.write(NotificationChannelGroupProto.DESCRIPTION, mDescription);
    341         proto.write(NotificationChannelGroupProto.IS_BLOCKED, mBlocked);
    342         for (NotificationChannel channel : mChannels) {
    343             channel.writeToProto(proto, NotificationChannelGroupProto.CHANNELS);
    344         }
    345         proto.end(token);
    346     }
    347 }
    348