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 */ NotificationChannel(String id, CharSequence name, @Importance int importance)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 */ NotificationChannel(Parcel in)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 writeToParcel(Parcel dest, int flags)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 */ lockFields(int field)261 public void lockFields(int field) { 262 mUserLockedFields |= field; 263 } 264 265 /** 266 * @hide 267 */ unlockFields(int field)268 public void unlockFields(int field) { 269 mUserLockedFields &= ~field; 270 } 271 272 /** 273 * @hide 274 */ setDeleted(boolean deleted)275 public void setDeleted(boolean deleted) { 276 mDeleted = deleted; 277 } 278 279 /** 280 * @hide 281 */ setBlockableSystem(boolean blockableSystem)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 */ setName(CharSequence name)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 */ setDescription(String description)303 public void setDescription(String description) { 304 mDesc = getTrimmedString(description); 305 } 306 getTrimmedString(String input)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 */ setGroup(String groupId)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 */ setShowBadge(boolean showBadge)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 */ setSound(Uri sound, AudioAttributes audioAttributes)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 */ enableLights(boolean lights)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 */ setLightColor(int argb)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 */ enableVibration(boolean vibration)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 */ setVibrationPattern(long[] vibrationPattern)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 */ setImportance(@mportance int importance)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 */ setBypassDnd(boolean bypassDnd)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 */ setLockscreenVisibility(int lockscreenVisibility)430 public void setLockscreenVisibility(int lockscreenVisibility) { 431 this.mLockscreenVisibility = lockscreenVisibility; 432 } 433 434 /** 435 * Returns the id of this channel. 436 */ getId()437 public String getId() { 438 return mId; 439 } 440 441 /** 442 * Returns the user visible name of this channel. 443 */ getName()444 public CharSequence getName() { 445 return mName; 446 } 447 448 /** 449 * Returns the user visible description of this channel. 450 */ getDescription()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 */ getImportance()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 */ canBypassDnd()467 public boolean canBypassDnd() { 468 return mBypassDnd; 469 } 470 471 /** 472 * Returns the notification sound for this channel. 473 */ getSound()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 */ getAudioAttributes()481 public AudioAttributes getAudioAttributes() { 482 return mAudioAttributes; 483 } 484 485 /** 486 * Returns whether notifications posted to this channel trigger notification lights. 487 */ shouldShowLights()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 */ getLightColor()496 public int getLightColor() { 497 return mLightColor; 498 } 499 500 /** 501 * Returns whether notifications posted to this channel always vibrate. 502 */ shouldVibrate()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 */ getVibrationPattern()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 */ getLockscreenVisibility()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 */ canShowBadge()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 */ getGroup()538 public String getGroup() { 539 return mGroup; 540 } 541 542 /** 543 * @hide 544 */ 545 @SystemApi isDeleted()546 public boolean isDeleted() { 547 return mDeleted; 548 } 549 550 /** 551 * @hide 552 */ 553 @SystemApi getUserLockedFields()554 public int getUserLockedFields() { 555 return mUserLockedFields; 556 } 557 558 /** 559 * @hide 560 */ isBlockableSystem()561 public boolean isBlockableSystem() { 562 return mBlockableSystem; 563 } 564 565 /** 566 * @hide 567 */ 568 @SystemApi populateFromXml(XmlPullParser parser)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 writeXml(XmlSerializer out)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 toJson()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 safeAudioAttributes(XmlPullParser parser)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 safeUri(XmlPullParser parser, String att)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 safeInt(XmlPullParser parser, String att, int defValue)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 tryParseInt(String value, int defValue)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 safeBool(XmlPullParser parser, String att, boolean defValue)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 safeLongArray(XmlPullParser parser, String att, long[] defValue)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 longArrayToString(long[] values)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 describeContents()768 public int describeContents() { 769 return 0; 770 } 771 772 @Override equals(Object o)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 hashCode()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 toString()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