Home | History | Annotate | Download | only in app
      1 /*
      2  * Copyright (C) 2016 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 org.json.JSONException;
     19 import org.json.JSONObject;
     20 import org.xmlpull.v1.XmlPullParser;
     21 import org.xmlpull.v1.XmlSerializer;
     22 
     23 import android.annotation.SystemApi;
     24 import android.app.NotificationManager.Importance;
     25 import android.content.Intent;
     26 import android.media.AudioAttributes;
     27 import android.net.Uri;
     28 import android.os.Parcel;
     29 import android.os.Parcelable;
     30 import android.provider.Settings;
     31 import android.service.notification.NotificationListenerService;
     32 import android.text.TextUtils;
     33 
     34 import java.io.IOException;
     35 import java.util.Arrays;
     36 
     37 /**
     38  * A representation of settings that apply to a collection of similarly themed notifications.
     39  */
     40 public final class NotificationChannel implements Parcelable {
     41 
     42     /**
     43      * The id of the default channel for an app. This id is reserved by the system. All
     44      * notifications posted from apps targeting {@link android.os.Build.VERSION_CODES#N_MR1} or
     45      * earlier without a notification channel specified are posted to this channel.
     46      */
     47     public static final String DEFAULT_CHANNEL_ID = "miscellaneous";
     48 
     49     /**
     50      * The maximum length for text fields in a NotificationChannel. Fields will be truncated at this
     51      * limit.
     52      */
     53     private static final int MAX_TEXT_LENGTH = 1000;
     54 
     55     private static final String TAG_CHANNEL = "channel";
     56     private static final String ATT_NAME = "name";
     57     private static final String ATT_DESC = "desc";
     58     private static final String ATT_ID = "id";
     59     private static final String ATT_DELETED = "deleted";
     60     private static final String ATT_PRIORITY = "priority";
     61     private static final String ATT_VISIBILITY = "visibility";
     62     private static final String ATT_IMPORTANCE = "importance";
     63     private static final String ATT_LIGHTS = "lights";
     64     private static final String ATT_LIGHT_COLOR = "light_color";
     65     private static final String ATT_VIBRATION = "vibration";
     66     private static final String ATT_VIBRATION_ENABLED = "vibration_enabled";
     67     private static final String ATT_SOUND = "sound";
     68     private static final String ATT_USAGE = "usage";
     69     private static final String ATT_FLAGS = "flags";
     70     private static final String ATT_CONTENT_TYPE = "content_type";
     71     private static final String ATT_SHOW_BADGE = "show_badge";
     72     private static final String ATT_USER_LOCKED = "locked";
     73     private static final String ATT_GROUP = "group";
     74     private static final String ATT_BLOCKABLE_SYSTEM = "blockable_system";
     75     private static final String DELIMITER = ",";
     76 
     77     /**
     78      * @hide
     79      */
     80     public static final int USER_LOCKED_PRIORITY = 0x00000001;
     81     /**
     82      * @hide
     83      */
     84     public static final int USER_LOCKED_VISIBILITY = 0x00000002;
     85     /**
     86      * @hide
     87      */
     88     public static final int USER_LOCKED_IMPORTANCE = 0x00000004;
     89     /**
     90      * @hide
     91      */
     92     public static final int USER_LOCKED_LIGHTS = 0x00000008;
     93     /**
     94      * @hide
     95      */
     96     public static final int USER_LOCKED_VIBRATION = 0x00000010;
     97     /**
     98      * @hide
     99      */
    100     public static final int USER_LOCKED_SOUND = 0x00000020;
    101 
    102     /**
    103      * @hide
    104      */
    105     public static final int USER_LOCKED_SHOW_BADGE = 0x00000080;
    106 
    107     /**
    108      * @hide
    109      */
    110     public static final int[] LOCKABLE_FIELDS = new int[] {
    111             USER_LOCKED_PRIORITY,
    112             USER_LOCKED_VISIBILITY,
    113             USER_LOCKED_IMPORTANCE,
    114             USER_LOCKED_LIGHTS,
    115             USER_LOCKED_VIBRATION,
    116             USER_LOCKED_SOUND,
    117             USER_LOCKED_SHOW_BADGE,
    118     };
    119 
    120     private static final int DEFAULT_LIGHT_COLOR = 0;
    121     private static final int DEFAULT_VISIBILITY =
    122             NotificationManager.VISIBILITY_NO_OVERRIDE;
    123     private static final int DEFAULT_IMPORTANCE =
    124             NotificationManager.IMPORTANCE_UNSPECIFIED;
    125     private static final boolean DEFAULT_DELETED = false;
    126     private static final boolean DEFAULT_SHOW_BADGE = true;
    127 
    128     private final String mId;
    129     private String mName;
    130     private String mDesc;
    131     private int mImportance = DEFAULT_IMPORTANCE;
    132     private boolean mBypassDnd;
    133     private int mLockscreenVisibility = DEFAULT_VISIBILITY;
    134     private Uri mSound = Settings.System.DEFAULT_NOTIFICATION_URI;
    135     private boolean mLights;
    136     private int mLightColor = DEFAULT_LIGHT_COLOR;
    137     private long[] mVibration;
    138     private int mUserLockedFields;
    139     private boolean mVibrationEnabled;
    140     private boolean mShowBadge = DEFAULT_SHOW_BADGE;
    141     private boolean mDeleted = DEFAULT_DELETED;
    142     private String mGroup;
    143     private AudioAttributes mAudioAttributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
    144     private boolean mBlockableSystem = false;
    145 
    146     /**
    147      * Creates a notification channel.
    148      *
    149      * @param id The id of the channel. Must be unique per package. The value may be truncated if
    150      *           it is too long.
    151      * @param name The user visible name of the channel. You can rename this channel when the system
    152      *             locale changes by listening for the {@link Intent#ACTION_LOCALE_CHANGED}
    153      *             broadcast. The recommended maximum length is 40 characters; the value may be
    154      *             truncated if it is too long.
    155      * @param importance The importance of the channel. This controls how interruptive notifications
    156      *                   posted to this channel are.
    157      */
    158     public NotificationChannel(String id, CharSequence name, @Importance int importance) {
    159         this.mId = getTrimmedString(id);
    160         this.mName = name != null ? getTrimmedString(name.toString()) : null;
    161         this.mImportance = importance;
    162     }
    163 
    164     /**
    165      * @hide
    166      */
    167     protected NotificationChannel(Parcel in) {
    168         if (in.readByte() != 0) {
    169             mId = in.readString();
    170         } else {
    171             mId = null;
    172         }
    173         if (in.readByte() != 0) {
    174             mName = in.readString();
    175         } else {
    176             mName = null;
    177         }
    178         if (in.readByte() != 0) {
    179             mDesc = in.readString();
    180         } else {
    181             mDesc = null;
    182         }
    183         mImportance = in.readInt();
    184         mBypassDnd = in.readByte() != 0;
    185         mLockscreenVisibility = in.readInt();
    186         if (in.readByte() != 0) {
    187             mSound = Uri.CREATOR.createFromParcel(in);
    188         } else {
    189             mSound = null;
    190         }
    191         mLights = in.readByte() != 0;
    192         mVibration = in.createLongArray();
    193         mUserLockedFields = in.readInt();
    194         mVibrationEnabled = in.readByte() != 0;
    195         mShowBadge = in.readByte() != 0;
    196         mDeleted = in.readByte() != 0;
    197         if (in.readByte() != 0) {
    198             mGroup = in.readString();
    199         } else {
    200             mGroup = null;
    201         }
    202         mAudioAttributes = in.readInt() > 0 ? AudioAttributes.CREATOR.createFromParcel(in) : null;
    203         mLightColor = in.readInt();
    204         mBlockableSystem = in.readBoolean();
    205     }
    206 
    207     @Override
    208     public void writeToParcel(Parcel dest, int flags) {
    209         if (mId != null) {
    210             dest.writeByte((byte) 1);
    211             dest.writeString(mId);
    212         } else {
    213             dest.writeByte((byte) 0);
    214         }
    215         if (mName != null) {
    216             dest.writeByte((byte) 1);
    217             dest.writeString(mName);
    218         } else {
    219             dest.writeByte((byte) 0);
    220         }
    221         if (mDesc != null) {
    222             dest.writeByte((byte) 1);
    223             dest.writeString(mDesc);
    224         } else {
    225             dest.writeByte((byte) 0);
    226         }
    227         dest.writeInt(mImportance);
    228         dest.writeByte(mBypassDnd ? (byte) 1 : (byte) 0);
    229         dest.writeInt(mLockscreenVisibility);
    230         if (mSound != null) {
    231             dest.writeByte((byte) 1);
    232             mSound.writeToParcel(dest, 0);
    233         } else {
    234             dest.writeByte((byte) 0);
    235         }
    236         dest.writeByte(mLights ? (byte) 1 : (byte) 0);
    237         dest.writeLongArray(mVibration);
    238         dest.writeInt(mUserLockedFields);
    239         dest.writeByte(mVibrationEnabled ? (byte) 1 : (byte) 0);
    240         dest.writeByte(mShowBadge ? (byte) 1 : (byte) 0);
    241         dest.writeByte(mDeleted ? (byte) 1 : (byte) 0);
    242         if (mGroup != null) {
    243             dest.writeByte((byte) 1);
    244             dest.writeString(mGroup);
    245         } else {
    246             dest.writeByte((byte) 0);
    247         }
    248         if (mAudioAttributes != null) {
    249             dest.writeInt(1);
    250             mAudioAttributes.writeToParcel(dest, 0);
    251         } else {
    252             dest.writeInt(0);
    253         }
    254         dest.writeInt(mLightColor);
    255         dest.writeBoolean(mBlockableSystem);
    256     }
    257 
    258     /**
    259      * @hide
    260      */
    261     public void lockFields(int field) {
    262         mUserLockedFields |= field;
    263     }
    264 
    265     /**
    266      * @hide
    267      */
    268     public void unlockFields(int field) {
    269         mUserLockedFields &= ~field;
    270     }
    271 
    272     /**
    273      * @hide
    274      */
    275     public void setDeleted(boolean deleted) {
    276         mDeleted = deleted;
    277     }
    278 
    279     /**
    280      * @hide
    281      */
    282     public void setBlockableSystem(boolean blockableSystem) {
    283         mBlockableSystem = blockableSystem;
    284     }
    285     // Modifiable by apps post channel creation
    286 
    287     /**
    288      * Sets the user visible name of this channel.
    289      *
    290      * <p>The recommended maximum length is 40 characters; the value may be truncated if it is too
    291      * long.
    292      */
    293     public void setName(CharSequence name) {
    294         mName = name != null ? getTrimmedString(name.toString()) : null;
    295     }
    296 
    297     /**
    298      * Sets the user visible description of this channel.
    299      *
    300      * <p>The recommended maximum length is 300 characters; the value may be truncated if it is too
    301      * long.
    302      */
    303     public void setDescription(String description) {
    304         mDesc = getTrimmedString(description);
    305     }
    306 
    307     private String getTrimmedString(String input) {
    308         if (input != null && input.length() > MAX_TEXT_LENGTH) {
    309             return input.substring(0, MAX_TEXT_LENGTH);
    310         }
    311         return input;
    312     }
    313 
    314     // Modifiable by apps on channel creation.
    315 
    316     /**
    317      * Sets what group this channel belongs to.
    318      *
    319      * Group information is only used for presentation, not for behavior.
    320      *
    321      * Only modifiable before the channel is submitted to
    322      * {@link NotificationManager#notify(String, int, Notification)}.
    323      *
    324      * @param groupId the id of a group created by
    325      * {@link NotificationManager#createNotificationChannelGroup(NotificationChannelGroup)}.
    326      */
    327     public void setGroup(String groupId) {
    328         this.mGroup = groupId;
    329     }
    330 
    331     /**
    332      * Sets whether notifications posted to this channel can appear as application icon badges
    333      * in a Launcher.
    334      *
    335      * @param showBadge true if badges should be allowed to be shown.
    336      */
    337     public void setShowBadge(boolean showBadge) {
    338         this.mShowBadge = showBadge;
    339     }
    340 
    341     /**
    342      * Sets the sound that should be played for notifications posted to this channel and its
    343      * audio attributes. Notification channels with an {@link #getImportance() importance} of at
    344      * least {@link NotificationManager#IMPORTANCE_DEFAULT} should have a sound.
    345      *
    346      * Only modifiable before the channel is submitted to
    347      * {@link NotificationManager#notify(String, int, Notification)}.
    348      */
    349     public void setSound(Uri sound, AudioAttributes audioAttributes) {
    350         this.mSound = sound;
    351         this.mAudioAttributes = audioAttributes;
    352     }
    353 
    354     /**
    355      * Sets whether notifications posted to this channel should display notification lights,
    356      * on devices that support that feature.
    357      *
    358      * Only modifiable before the channel is submitted to
    359      * {@link NotificationManager#notify(String, int, Notification)}.
    360      */
    361     public void enableLights(boolean lights) {
    362         this.mLights = lights;
    363     }
    364 
    365     /**
    366      * Sets the notification light color for notifications posted to this channel, if lights are
    367      * {@link #enableLights(boolean) enabled} on this channel and the device supports that feature.
    368      *
    369      * Only modifiable before the channel is submitted to
    370      * {@link NotificationManager#notify(String, int, Notification)}.
    371      */
    372     public void setLightColor(int argb) {
    373         this.mLightColor = argb;
    374     }
    375 
    376     /**
    377      * Sets whether notification posted to this channel should vibrate. The vibration pattern can
    378      * be set with {@link #setVibrationPattern(long[])}.
    379      *
    380      * Only modifiable before the channel is submitted to
    381      * {@link NotificationManager#notify(String, int, Notification)}.
    382      */
    383     public void enableVibration(boolean vibration) {
    384         this.mVibrationEnabled = vibration;
    385     }
    386 
    387     /**
    388      * Sets the vibration pattern for notifications posted to this channel. If the provided
    389      * pattern is valid (non-null, non-empty), will {@link #enableVibration(boolean)} enable
    390      * vibration} as well. Otherwise, vibration will be disabled.
    391      *
    392      * Only modifiable before the channel is submitted to
    393      * {@link NotificationManager#notify(String, int, Notification)}.
    394      */
    395     public void setVibrationPattern(long[] vibrationPattern) {
    396         this.mVibrationEnabled = vibrationPattern != null && vibrationPattern.length > 0;
    397         this.mVibration = vibrationPattern;
    398     }
    399 
    400     /**
    401      * Sets the level of interruption of this notification channel. Only
    402      * modifiable before the channel is submitted to
    403      * {@link NotificationManager#notify(String, int, Notification)}.
    404      *
    405      * @param importance the amount the user should be interrupted by
    406      *            notifications from this channel.
    407      */
    408     public void setImportance(@Importance int importance) {
    409         this.mImportance = importance;
    410     }
    411 
    412     // Modifiable by a notification ranker.
    413 
    414     /**
    415      * Sets whether or not notifications posted to this channel can interrupt the user in
    416      * {@link android.app.NotificationManager.Policy#INTERRUPTION_FILTER_PRIORITY} mode.
    417      *
    418      * Only modifiable by the system and notification ranker.
    419      */
    420     public void setBypassDnd(boolean bypassDnd) {
    421         this.mBypassDnd = bypassDnd;
    422     }
    423 
    424     /**
    425      * Sets whether notifications posted to this channel appear on the lockscreen or not, and if so,
    426      * whether they appear in a redacted form. See e.g. {@link Notification#VISIBILITY_SECRET}.
    427      *
    428      * Only modifiable by the system and notification ranker.
    429      */
    430     public void setLockscreenVisibility(int lockscreenVisibility) {
    431         this.mLockscreenVisibility = lockscreenVisibility;
    432     }
    433 
    434     /**
    435      * Returns the id of this channel.
    436      */
    437     public String getId() {
    438         return mId;
    439     }
    440 
    441     /**
    442      * Returns the user visible name of this channel.
    443      */
    444     public CharSequence getName() {
    445         return mName;
    446     }
    447 
    448     /**
    449      * Returns the user visible description of this channel.
    450      */
    451     public String getDescription() {
    452         return mDesc;
    453     }
    454 
    455     /**
    456      * Returns the user specified importance e.g. {@link NotificationManager#IMPORTANCE_LOW} for
    457      * notifications posted to this channel.
    458      */
    459     public int getImportance() {
    460         return mImportance;
    461     }
    462 
    463     /**
    464      * Whether or not notifications posted to this channel can bypass the Do Not Disturb
    465      * {@link NotificationManager#INTERRUPTION_FILTER_PRIORITY} mode.
    466      */
    467     public boolean canBypassDnd() {
    468         return mBypassDnd;
    469     }
    470 
    471     /**
    472      * Returns the notification sound for this channel.
    473      */
    474     public Uri getSound() {
    475         return mSound;
    476     }
    477 
    478     /**
    479      * Returns the audio attributes for sound played by notifications posted to this channel.
    480      */
    481     public AudioAttributes getAudioAttributes() {
    482         return mAudioAttributes;
    483     }
    484 
    485     /**
    486      * Returns whether notifications posted to this channel trigger notification lights.
    487      */
    488     public boolean shouldShowLights() {
    489         return mLights;
    490     }
    491 
    492     /**
    493      * Returns the notification light color for notifications posted to this channel. Irrelevant
    494      * unless {@link #shouldShowLights()}.
    495      */
    496     public int getLightColor() {
    497         return mLightColor;
    498     }
    499 
    500     /**
    501      * Returns whether notifications posted to this channel always vibrate.
    502      */
    503     public boolean shouldVibrate() {
    504         return mVibrationEnabled;
    505     }
    506 
    507     /**
    508      * Returns the vibration pattern for notifications posted to this channel. Will be ignored if
    509      * vibration is not enabled ({@link #shouldVibrate()}.
    510      */
    511     public long[] getVibrationPattern() {
    512         return mVibration;
    513     }
    514 
    515     /**
    516      * Returns whether or not notifications posted to this channel are shown on the lockscreen in
    517      * full or redacted form.
    518      */
    519     public int getLockscreenVisibility() {
    520         return mLockscreenVisibility;
    521     }
    522 
    523     /**
    524      * Returns whether notifications posted to this channel can appear as badges in a Launcher
    525      * application.
    526      *
    527      * Note that badging may be disabled for other reasons.
    528      */
    529     public boolean canShowBadge() {
    530         return mShowBadge;
    531     }
    532 
    533     /**
    534      * Returns what group this channel belongs to.
    535      *
    536      * This is used only for visually grouping channels in the UI.
    537      */
    538     public String getGroup() {
    539         return mGroup;
    540     }
    541 
    542     /**
    543      * @hide
    544      */
    545     @SystemApi
    546     public boolean isDeleted() {
    547         return mDeleted;
    548     }
    549 
    550     /**
    551      * @hide
    552      */
    553     @SystemApi
    554     public int getUserLockedFields() {
    555         return mUserLockedFields;
    556     }
    557 
    558     /**
    559      * @hide
    560      */
    561     public boolean isBlockableSystem() {
    562         return mBlockableSystem;
    563     }
    564 
    565     /**
    566      * @hide
    567      */
    568     @SystemApi
    569     public void populateFromXml(XmlPullParser parser) {
    570         // Name, id, and importance are set in the constructor.
    571         setDescription(parser.getAttributeValue(null, ATT_DESC));
    572         setBypassDnd(Notification.PRIORITY_DEFAULT
    573                 != safeInt(parser, ATT_PRIORITY, Notification.PRIORITY_DEFAULT));
    574         setLockscreenVisibility(safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY));
    575         setSound(safeUri(parser, ATT_SOUND), safeAudioAttributes(parser));
    576         enableLights(safeBool(parser, ATT_LIGHTS, false));
    577         setLightColor(safeInt(parser, ATT_LIGHT_COLOR, DEFAULT_LIGHT_COLOR));
    578         setVibrationPattern(safeLongArray(parser, ATT_VIBRATION, null));
    579         enableVibration(safeBool(parser, ATT_VIBRATION_ENABLED, false));
    580         setShowBadge(safeBool(parser, ATT_SHOW_BADGE, false));
    581         setDeleted(safeBool(parser, ATT_DELETED, false));
    582         setGroup(parser.getAttributeValue(null, ATT_GROUP));
    583         lockFields(safeInt(parser, ATT_USER_LOCKED, 0));
    584         setBlockableSystem(safeBool(parser, ATT_BLOCKABLE_SYSTEM, false));
    585     }
    586 
    587     /**
    588      * @hide
    589      */
    590     @SystemApi
    591     public void writeXml(XmlSerializer out) throws IOException {
    592         out.startTag(null, TAG_CHANNEL);
    593         out.attribute(null, ATT_ID, getId());
    594         if (getName() != null) {
    595             out.attribute(null, ATT_NAME, getName().toString());
    596         }
    597         if (getDescription() != null) {
    598             out.attribute(null, ATT_DESC, getDescription());
    599         }
    600         if (getImportance() != DEFAULT_IMPORTANCE) {
    601             out.attribute(
    602                     null, ATT_IMPORTANCE, Integer.toString(getImportance()));
    603         }
    604         if (canBypassDnd()) {
    605             out.attribute(
    606                     null, ATT_PRIORITY, Integer.toString(Notification.PRIORITY_MAX));
    607         }
    608         if (getLockscreenVisibility() != DEFAULT_VISIBILITY) {
    609             out.attribute(null, ATT_VISIBILITY,
    610                     Integer.toString(getLockscreenVisibility()));
    611         }
    612         if (getSound() != null) {
    613             out.attribute(null, ATT_SOUND, getSound().toString());
    614         }
    615         if (getAudioAttributes() != null) {
    616             out.attribute(null, ATT_USAGE, Integer.toString(getAudioAttributes().getUsage()));
    617             out.attribute(null, ATT_CONTENT_TYPE,
    618                     Integer.toString(getAudioAttributes().getContentType()));
    619             out.attribute(null, ATT_FLAGS, Integer.toString(getAudioAttributes().getFlags()));
    620         }
    621         if (shouldShowLights()) {
    622             out.attribute(null, ATT_LIGHTS, Boolean.toString(shouldShowLights()));
    623         }
    624         if (getLightColor() != DEFAULT_LIGHT_COLOR) {
    625             out.attribute(null, ATT_LIGHT_COLOR, Integer.toString(getLightColor()));
    626         }
    627         if (shouldVibrate()) {
    628             out.attribute(null, ATT_VIBRATION_ENABLED, Boolean.toString(shouldVibrate()));
    629         }
    630         if (getVibrationPattern() != null) {
    631             out.attribute(null, ATT_VIBRATION, longArrayToString(getVibrationPattern()));
    632         }
    633         if (getUserLockedFields() != 0) {
    634             out.attribute(null, ATT_USER_LOCKED, Integer.toString(getUserLockedFields()));
    635         }
    636         if (canShowBadge()) {
    637             out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(canShowBadge()));
    638         }
    639         if (isDeleted()) {
    640             out.attribute(null, ATT_DELETED, Boolean.toString(isDeleted()));
    641         }
    642         if (getGroup() != null) {
    643             out.attribute(null, ATT_GROUP, getGroup());
    644         }
    645         if (isBlockableSystem()) {
    646             out.attribute(null, ATT_BLOCKABLE_SYSTEM, Boolean.toString(isBlockableSystem()));
    647         }
    648 
    649         out.endTag(null, TAG_CHANNEL);
    650     }
    651 
    652     /**
    653      * @hide
    654      */
    655     @SystemApi
    656     public JSONObject toJson() throws JSONException {
    657         JSONObject record = new JSONObject();
    658         record.put(ATT_ID, getId());
    659         record.put(ATT_NAME, getName());
    660         record.put(ATT_DESC, getDescription());
    661         if (getImportance() != DEFAULT_IMPORTANCE) {
    662             record.put(ATT_IMPORTANCE,
    663                     NotificationListenerService.Ranking.importanceToString(getImportance()));
    664         }
    665         if (canBypassDnd()) {
    666             record.put(ATT_PRIORITY, Notification.PRIORITY_MAX);
    667         }
    668         if (getLockscreenVisibility() != DEFAULT_VISIBILITY) {
    669             record.put(ATT_VISIBILITY, Notification.visibilityToString(getLockscreenVisibility()));
    670         }
    671         if (getSound() != null) {
    672             record.put(ATT_SOUND, getSound().toString());
    673         }
    674         if (getAudioAttributes() != null) {
    675             record.put(ATT_USAGE, Integer.toString(getAudioAttributes().getUsage()));
    676             record.put(ATT_CONTENT_TYPE,
    677                     Integer.toString(getAudioAttributes().getContentType()));
    678             record.put(ATT_FLAGS, Integer.toString(getAudioAttributes().getFlags()));
    679         }
    680         record.put(ATT_LIGHTS, Boolean.toString(shouldShowLights()));
    681         record.put(ATT_LIGHT_COLOR, Integer.toString(getLightColor()));
    682         record.put(ATT_VIBRATION_ENABLED, Boolean.toString(shouldVibrate()));
    683         record.put(ATT_USER_LOCKED, Integer.toString(getUserLockedFields()));
    684         record.put(ATT_VIBRATION, longArrayToString(getVibrationPattern()));
    685         record.put(ATT_SHOW_BADGE, Boolean.toString(canShowBadge()));
    686         record.put(ATT_DELETED, Boolean.toString(isDeleted()));
    687         record.put(ATT_GROUP, getGroup());
    688         record.put(ATT_BLOCKABLE_SYSTEM, isBlockableSystem());
    689         return record;
    690     }
    691 
    692     private static AudioAttributes safeAudioAttributes(XmlPullParser parser) {
    693         int usage = safeInt(parser, ATT_USAGE, AudioAttributes.USAGE_NOTIFICATION);
    694         int contentType = safeInt(parser, ATT_CONTENT_TYPE,
    695                 AudioAttributes.CONTENT_TYPE_SONIFICATION);
    696         int flags = safeInt(parser, ATT_FLAGS, 0);
    697         return new AudioAttributes.Builder()
    698                 .setUsage(usage)
    699                 .setContentType(contentType)
    700                 .setFlags(flags)
    701                 .build();
    702     }
    703 
    704     private static Uri safeUri(XmlPullParser parser, String att) {
    705         final String val = parser.getAttributeValue(null, att);
    706         return val == null ? null : Uri.parse(val);
    707     }
    708 
    709     private static int safeInt(XmlPullParser parser, String att, int defValue) {
    710         final String val = parser.getAttributeValue(null, att);
    711         return tryParseInt(val, defValue);
    712     }
    713 
    714     private static int tryParseInt(String value, int defValue) {
    715         if (TextUtils.isEmpty(value)) return defValue;
    716         try {
    717             return Integer.parseInt(value);
    718         } catch (NumberFormatException e) {
    719             return defValue;
    720         }
    721     }
    722 
    723     private static boolean safeBool(XmlPullParser parser, String att, boolean defValue) {
    724         final String value = parser.getAttributeValue(null, att);
    725         if (TextUtils.isEmpty(value)) return defValue;
    726         return Boolean.parseBoolean(value);
    727     }
    728 
    729     private static long[] safeLongArray(XmlPullParser parser, String att, long[] defValue) {
    730         final String attributeValue = parser.getAttributeValue(null, att);
    731         if (TextUtils.isEmpty(attributeValue)) return defValue;
    732         String[] values = attributeValue.split(DELIMITER);
    733         long[] longValues = new long[values.length];
    734         for (int i = 0; i < values.length; i++) {
    735             try {
    736                 longValues[i] = Long.parseLong(values[i]);
    737             } catch (NumberFormatException e) {
    738                 longValues[i] = 0;
    739             }
    740         }
    741         return longValues;
    742     }
    743 
    744     private static String longArrayToString(long[] values) {
    745         StringBuffer sb = new StringBuffer();
    746         if (values != null) {
    747             for (int i = 0; i < values.length - 1; i++) {
    748                 sb.append(values[i]).append(DELIMITER);
    749             }
    750             sb.append(values[values.length - 1]);
    751         }
    752         return sb.toString();
    753     }
    754 
    755     public static final Creator<NotificationChannel> CREATOR = new Creator<NotificationChannel>() {
    756         @Override
    757         public NotificationChannel createFromParcel(Parcel in) {
    758             return new NotificationChannel(in);
    759         }
    760 
    761         @Override
    762         public NotificationChannel[] newArray(int size) {
    763             return new NotificationChannel[size];
    764         }
    765     };
    766 
    767     @Override
    768     public int describeContents() {
    769         return 0;
    770     }
    771 
    772     @Override
    773     public boolean equals(Object o) {
    774         if (this == o) return true;
    775         if (o == null || getClass() != o.getClass()) return false;
    776 
    777         NotificationChannel that = (NotificationChannel) o;
    778 
    779         if (getImportance() != that.getImportance()) return false;
    780         if (mBypassDnd != that.mBypassDnd) return false;
    781         if (getLockscreenVisibility() != that.getLockscreenVisibility()) return false;
    782         if (mLights != that.mLights) return false;
    783         if (getLightColor() != that.getLightColor()) return false;
    784         if (getUserLockedFields() != that.getUserLockedFields()) return false;
    785         if (mVibrationEnabled != that.mVibrationEnabled) return false;
    786         if (mShowBadge != that.mShowBadge) return false;
    787         if (isDeleted() != that.isDeleted()) return false;
    788         if (isBlockableSystem() != that.isBlockableSystem()) return false;
    789         if (getId() != null ? !getId().equals(that.getId()) : that.getId() != null) return false;
    790         if (getName() != null ? !getName().equals(that.getName()) : that.getName() != null) {
    791             return false;
    792         }
    793         if (getDescription() != null ? !getDescription().equals(that.getDescription())
    794                 : that.getDescription() != null) {
    795             return false;
    796         }
    797         if (getSound() != null ? !getSound().equals(that.getSound()) : that.getSound() != null) {
    798             return false;
    799         }
    800         if (!Arrays.equals(mVibration, that.mVibration)) return false;
    801         if (getGroup() != null ? !getGroup().equals(that.getGroup()) : that.getGroup() != null) {
    802             return false;
    803         }
    804         return getAudioAttributes() != null ? getAudioAttributes().equals(that.getAudioAttributes())
    805                 : that.getAudioAttributes() == null;
    806 
    807     }
    808 
    809     @Override
    810     public int hashCode() {
    811         int result = getId() != null ? getId().hashCode() : 0;
    812         result = 31 * result + (getName() != null ? getName().hashCode() : 0);
    813         result = 31 * result + (getDescription() != null ? getDescription().hashCode() : 0);
    814         result = 31 * result + getImportance();
    815         result = 31 * result + (mBypassDnd ? 1 : 0);
    816         result = 31 * result + getLockscreenVisibility();
    817         result = 31 * result + (getSound() != null ? getSound().hashCode() : 0);
    818         result = 31 * result + (mLights ? 1 : 0);
    819         result = 31 * result + getLightColor();
    820         result = 31 * result + Arrays.hashCode(mVibration);
    821         result = 31 * result + getUserLockedFields();
    822         result = 31 * result + (mVibrationEnabled ? 1 : 0);
    823         result = 31 * result + (mShowBadge ? 1 : 0);
    824         result = 31 * result + (isDeleted() ? 1 : 0);
    825         result = 31 * result + (getGroup() != null ? getGroup().hashCode() : 0);
    826         result = 31 * result + (getAudioAttributes() != null ? getAudioAttributes().hashCode() : 0);
    827         result = 31 * result + (isBlockableSystem() ? 1 : 0);
    828         return result;
    829     }
    830 
    831     @Override
    832     public String toString() {
    833         return "NotificationChannel{" +
    834                 "mId='" + mId + '\'' +
    835                 ", mName=" + mName +
    836                 ", mDescription=" + (!TextUtils.isEmpty(mDesc) ? "hasDescription " : "") +
    837                 ", mImportance=" + mImportance +
    838                 ", mBypassDnd=" + mBypassDnd +
    839                 ", mLockscreenVisibility=" + mLockscreenVisibility +
    840                 ", mSound=" + mSound +
    841                 ", mLights=" + mLights +
    842                 ", mLightColor=" + mLightColor +
    843                 ", mVibration=" + Arrays.toString(mVibration) +
    844                 ", mUserLockedFields=" + mUserLockedFields +
    845                 ", mVibrationEnabled=" + mVibrationEnabled +
    846                 ", mShowBadge=" + mShowBadge +
    847                 ", mDeleted=" + mDeleted +
    848                 ", mGroup='" + mGroup + '\'' +
    849                 ", mAudioAttributes=" + mAudioAttributes +
    850                 ", mBlockableSystem=" + mBlockableSystem +
    851                 '}';
    852     }
    853 }
    854