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 android.annotation.NonNull; 19 import android.annotation.Nullable; 20 import android.annotation.SystemApi; 21 import android.annotation.TestApi; 22 import android.app.NotificationManager.Importance; 23 import android.compat.annotation.UnsupportedAppUsage; 24 import android.content.ContentResolver; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.pm.ShortcutInfo; 28 import android.media.AudioAttributes; 29 import android.net.Uri; 30 import android.os.Parcel; 31 import android.os.Parcelable; 32 import android.provider.Settings; 33 import android.service.notification.NotificationListenerService; 34 import android.text.TextUtils; 35 import android.util.Slog; 36 import android.util.TypedXmlPullParser; 37 import android.util.TypedXmlSerializer; 38 import android.util.proto.ProtoOutputStream; 39 40 import com.android.internal.util.Preconditions; 41 import com.android.internal.util.XmlUtils; 42 43 import org.json.JSONException; 44 import org.json.JSONObject; 45 import org.xmlpull.v1.XmlPullParser; 46 import org.xmlpull.v1.XmlSerializer; 47 48 import java.io.IOException; 49 import java.io.PrintWriter; 50 import java.util.Arrays; 51 import java.util.Objects; 52 53 /** 54 * A representation of settings that apply to a collection of similarly themed notifications. 55 */ 56 public final class NotificationChannel implements Parcelable { 57 58 /** 59 * The id of the default channel for an app. This id is reserved by the system. All 60 * notifications posted from apps targeting {@link android.os.Build.VERSION_CODES#N_MR1} or 61 * earlier without a notification channel specified are posted to this channel. 62 */ 63 public static final String DEFAULT_CHANNEL_ID = "miscellaneous"; 64 65 /** 66 * The formatter used by the system to create an id for notification 67 * channels when it automatically creates conversation channels on behalf of an app. The format 68 * string takes two arguments, in this order: the 69 * {@link #getId()} of the original notification channel, and the 70 * {@link ShortcutInfo#getId() id} of the conversation. 71 * @hide 72 */ 73 public static final String CONVERSATION_CHANNEL_ID_FORMAT = "%1$s : %2$s"; 74 75 /** 76 * TODO: STOPSHIP remove 77 * Conversation id to use for apps that aren't providing them yet. 78 * @hide 79 */ 80 public static final String PLACEHOLDER_CONVERSATION_ID = ":placeholder_id"; 81 82 /** 83 * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields 84 * that have to do with editing sound, like a tone picker 85 * ({@link #setSound(Uri, AudioAttributes)}). 86 */ 87 public static final String EDIT_SOUND = "sound"; 88 /** 89 * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields 90 * that have to do with editing vibration ({@link #enableVibration(boolean)}, 91 * {@link #setVibrationPattern(long[])}). 92 */ 93 public static final String EDIT_VIBRATION = "vibration"; 94 /** 95 * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields 96 * that have to do with editing importance ({@link #setImportance(int)}) and/or conversation 97 * priority. 98 */ 99 public static final String EDIT_IMPORTANCE = "importance"; 100 /** 101 * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields 102 * that have to do with editing behavior on devices that are locked or have a turned off 103 * display ({@link #setLockscreenVisibility(int)}, {@link #enableLights(boolean)}, 104 * {@link #setLightColor(int)}). 105 */ 106 public static final String EDIT_LOCKED_DEVICE = "locked"; 107 /** 108 * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields 109 * that have to do with editing do not disturb bypass {(@link #setBypassDnd(boolean)}) . 110 */ 111 public static final String EDIT_ZEN = "zen"; 112 /** 113 * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields 114 * that have to do with editing conversation settings (demoting or restoring a channel to 115 * be a Conversation, changing bubble behavior, or setting the priority of a conversation). 116 */ 117 public static final String EDIT_CONVERSATION = "conversation"; 118 /** 119 * Extra value for {@link Settings#EXTRA_CHANNEL_FILTER_LIST}. Include to show fields 120 * that have to do with editing launcher behavior (showing badges)}. 121 */ 122 public static final String EDIT_LAUNCHER = "launcher"; 123 124 /** 125 * The maximum length for text fields in a NotificationChannel. Fields will be truncated at this 126 * limit. 127 * @hide 128 */ 129 public static final int MAX_TEXT_LENGTH = 1000; 130 /** 131 * @hide 132 */ 133 public static final int MAX_VIBRATION_LENGTH = 1000; 134 135 private static final String TAG_CHANNEL = "channel"; 136 private static final String ATT_NAME = "name"; 137 private static final String ATT_DESC = "desc"; 138 private static final String ATT_ID = "id"; 139 private static final String ATT_DELETED = "deleted"; 140 private static final String ATT_PRIORITY = "priority"; 141 private static final String ATT_VISIBILITY = "visibility"; 142 private static final String ATT_IMPORTANCE = "importance"; 143 private static final String ATT_LIGHTS = "lights"; 144 private static final String ATT_LIGHT_COLOR = "light_color"; 145 private static final String ATT_VIBRATION = "vibration"; 146 private static final String ATT_VIBRATION_ENABLED = "vibration_enabled"; 147 private static final String ATT_SOUND = "sound"; 148 private static final String ATT_USAGE = "usage"; 149 private static final String ATT_FLAGS = "flags"; 150 private static final String ATT_CONTENT_TYPE = "content_type"; 151 private static final String ATT_SHOW_BADGE = "show_badge"; 152 private static final String ATT_USER_LOCKED = "locked"; 153 private static final String ATT_FG_SERVICE_SHOWN = "fgservice"; 154 private static final String ATT_GROUP = "group"; 155 private static final String ATT_BLOCKABLE_SYSTEM = "blockable_system"; 156 private static final String ATT_ALLOW_BUBBLE = "allow_bubbles"; 157 private static final String ATT_ORIG_IMP = "orig_imp"; 158 private static final String ATT_PARENT_CHANNEL = "parent"; 159 private static final String ATT_CONVERSATION_ID = "conv_id"; 160 private static final String ATT_IMP_CONVERSATION = "imp_conv"; 161 private static final String ATT_DEMOTE = "dem"; 162 private static final String ATT_DELETED_TIME_MS = "del_time"; 163 private static final String DELIMITER = ","; 164 165 /** 166 * @hide 167 */ 168 public static final int USER_LOCKED_PRIORITY = 0x00000001; 169 /** 170 * @hide 171 */ 172 public static final int USER_LOCKED_VISIBILITY = 0x00000002; 173 /** 174 * @hide 175 */ 176 public static final int USER_LOCKED_IMPORTANCE = 0x00000004; 177 /** 178 * @hide 179 */ 180 public static final int USER_LOCKED_LIGHTS = 0x00000008; 181 /** 182 * @hide 183 */ 184 public static final int USER_LOCKED_VIBRATION = 0x00000010; 185 /** 186 * @hide 187 */ 188 @SystemApi 189 public static final int USER_LOCKED_SOUND = 0x00000020; 190 191 /** 192 * @hide 193 */ 194 public static final int USER_LOCKED_SHOW_BADGE = 0x00000080; 195 196 /** 197 * @hide 198 */ 199 public static final int USER_LOCKED_ALLOW_BUBBLE = 0x00000100; 200 201 /** 202 * @hide 203 */ 204 public static final int[] LOCKABLE_FIELDS = new int[] { 205 USER_LOCKED_PRIORITY, 206 USER_LOCKED_VISIBILITY, 207 USER_LOCKED_IMPORTANCE, 208 USER_LOCKED_LIGHTS, 209 USER_LOCKED_VIBRATION, 210 USER_LOCKED_SOUND, 211 USER_LOCKED_SHOW_BADGE, 212 USER_LOCKED_ALLOW_BUBBLE 213 }; 214 215 /** 216 * @hide 217 */ 218 public static final int DEFAULT_ALLOW_BUBBLE = -1; 219 /** 220 * @hide 221 */ 222 public static final int ALLOW_BUBBLE_ON = 1; 223 /** 224 * @hide 225 */ 226 public static final int ALLOW_BUBBLE_OFF = 0; 227 228 private static final int DEFAULT_LIGHT_COLOR = 0; 229 private static final int DEFAULT_VISIBILITY = 230 NotificationManager.VISIBILITY_NO_OVERRIDE; 231 private static final int DEFAULT_IMPORTANCE = 232 NotificationManager.IMPORTANCE_UNSPECIFIED; 233 private static final boolean DEFAULT_DELETED = false; 234 private static final boolean DEFAULT_SHOW_BADGE = true; 235 private static final long DEFAULT_DELETION_TIME_MS = -1; 236 237 @UnsupportedAppUsage 238 private String mId; 239 private String mName; 240 private String mDesc; 241 private int mImportance = DEFAULT_IMPORTANCE; 242 private int mOriginalImportance = DEFAULT_IMPORTANCE; 243 private boolean mBypassDnd; 244 private int mLockscreenVisibility = DEFAULT_VISIBILITY; 245 private Uri mSound = Settings.System.DEFAULT_NOTIFICATION_URI; 246 private boolean mLights; 247 private int mLightColor = DEFAULT_LIGHT_COLOR; 248 private long[] mVibration; 249 // Bitwise representation of fields that have been changed by the user, preventing the app from 250 // making changes to these fields. 251 private int mUserLockedFields; 252 private boolean mFgServiceShown; 253 private boolean mVibrationEnabled; 254 private boolean mShowBadge = DEFAULT_SHOW_BADGE; 255 private boolean mDeleted = DEFAULT_DELETED; 256 private String mGroup; 257 private AudioAttributes mAudioAttributes = Notification.AUDIO_ATTRIBUTES_DEFAULT; 258 // If this is a blockable system notification channel. 259 private boolean mBlockableSystem = false; 260 private int mAllowBubbles = DEFAULT_ALLOW_BUBBLE; 261 private boolean mImportanceLockedDefaultApp; 262 private String mParentId = null; 263 private String mConversationId = null; 264 private boolean mDemoted = false; 265 private boolean mImportantConvo = false; 266 private long mDeletedTime = DEFAULT_DELETION_TIME_MS; 267 268 /** 269 * Creates a notification channel. 270 * 271 * @param id The id of the channel. Must be unique per package. The value may be truncated if 272 * it is too long. 273 * @param name The user visible name of the channel. You can rename this channel when the system 274 * locale changes by listening for the {@link Intent#ACTION_LOCALE_CHANGED} 275 * broadcast. The recommended maximum length is 40 characters; the value may be 276 * truncated if it is too long. 277 * @param importance The importance of the channel. This controls how interruptive notifications 278 * posted to this channel are. 279 */ NotificationChannel(String id, CharSequence name, @Importance int importance)280 public NotificationChannel(String id, CharSequence name, @Importance int importance) { 281 this.mId = getTrimmedString(id); 282 this.mName = name != null ? getTrimmedString(name.toString()) : null; 283 this.mImportance = importance; 284 } 285 286 /** 287 * @hide 288 */ NotificationChannel(Parcel in)289 protected NotificationChannel(Parcel in) { 290 if (in.readByte() != 0) { 291 mId = getTrimmedString(in.readString()); 292 } else { 293 mId = null; 294 } 295 if (in.readByte() != 0) { 296 mName = getTrimmedString(in.readString()); 297 } else { 298 mName = null; 299 } 300 if (in.readByte() != 0) { 301 mDesc = getTrimmedString(in.readString()); 302 } else { 303 mDesc = null; 304 } 305 mImportance = in.readInt(); 306 mBypassDnd = in.readByte() != 0; 307 mLockscreenVisibility = in.readInt(); 308 if (in.readByte() != 0) { 309 mSound = Uri.CREATOR.createFromParcel(in); 310 mSound = Uri.parse(getTrimmedString(mSound.toString())); 311 } else { 312 mSound = null; 313 } 314 mLights = in.readByte() != 0; 315 mVibration = in.createLongArray(); 316 if (mVibration != null && mVibration.length > MAX_VIBRATION_LENGTH) { 317 mVibration = Arrays.copyOf(mVibration, MAX_VIBRATION_LENGTH); 318 } 319 mUserLockedFields = in.readInt(); 320 mFgServiceShown = in.readByte() != 0; 321 mVibrationEnabled = in.readByte() != 0; 322 mShowBadge = in.readByte() != 0; 323 mDeleted = in.readByte() != 0; 324 if (in.readByte() != 0) { 325 mGroup = getTrimmedString(in.readString()); 326 } else { 327 mGroup = null; 328 } 329 mAudioAttributes = in.readInt() > 0 ? AudioAttributes.CREATOR.createFromParcel(in) : null; 330 mLightColor = in.readInt(); 331 mBlockableSystem = in.readBoolean(); 332 mAllowBubbles = in.readInt(); 333 mOriginalImportance = in.readInt(); 334 mParentId = getTrimmedString(in.readString()); 335 mConversationId = getTrimmedString(in.readString()); 336 mDemoted = in.readBoolean(); 337 mImportantConvo = in.readBoolean(); 338 mDeletedTime = in.readLong(); 339 mImportanceLockedDefaultApp = in.readBoolean(); 340 } 341 342 @Override writeToParcel(Parcel dest, int flags)343 public void writeToParcel(Parcel dest, int flags) { 344 if (mId != null) { 345 dest.writeByte((byte) 1); 346 dest.writeString(mId); 347 } else { 348 dest.writeByte((byte) 0); 349 } 350 if (mName != null) { 351 dest.writeByte((byte) 1); 352 dest.writeString(mName); 353 } else { 354 dest.writeByte((byte) 0); 355 } 356 if (mDesc != null) { 357 dest.writeByte((byte) 1); 358 dest.writeString(mDesc); 359 } else { 360 dest.writeByte((byte) 0); 361 } 362 dest.writeInt(mImportance); 363 dest.writeByte(mBypassDnd ? (byte) 1 : (byte) 0); 364 dest.writeInt(mLockscreenVisibility); 365 if (mSound != null) { 366 dest.writeByte((byte) 1); 367 mSound.writeToParcel(dest, 0); 368 } else { 369 dest.writeByte((byte) 0); 370 } 371 dest.writeByte(mLights ? (byte) 1 : (byte) 0); 372 dest.writeLongArray(mVibration); 373 dest.writeInt(mUserLockedFields); 374 dest.writeByte(mFgServiceShown ? (byte) 1 : (byte) 0); 375 dest.writeByte(mVibrationEnabled ? (byte) 1 : (byte) 0); 376 dest.writeByte(mShowBadge ? (byte) 1 : (byte) 0); 377 dest.writeByte(mDeleted ? (byte) 1 : (byte) 0); 378 if (mGroup != null) { 379 dest.writeByte((byte) 1); 380 dest.writeString(mGroup); 381 } else { 382 dest.writeByte((byte) 0); 383 } 384 if (mAudioAttributes != null) { 385 dest.writeInt(1); 386 mAudioAttributes.writeToParcel(dest, 0); 387 } else { 388 dest.writeInt(0); 389 } 390 dest.writeInt(mLightColor); 391 dest.writeBoolean(mBlockableSystem); 392 dest.writeInt(mAllowBubbles); 393 dest.writeInt(mOriginalImportance); 394 dest.writeString(mParentId); 395 dest.writeString(mConversationId); 396 dest.writeBoolean(mDemoted); 397 dest.writeBoolean(mImportantConvo); 398 dest.writeLong(mDeletedTime); 399 dest.writeBoolean(mImportanceLockedDefaultApp); 400 } 401 402 /** 403 * @hide 404 */ 405 @TestApi lockFields(int field)406 public void lockFields(int field) { 407 mUserLockedFields |= field; 408 } 409 410 /** 411 * @hide 412 */ unlockFields(int field)413 public void unlockFields(int field) { 414 mUserLockedFields &= ~field; 415 } 416 417 /** 418 * @hide 419 */ 420 @TestApi setFgServiceShown(boolean shown)421 public void setFgServiceShown(boolean shown) { 422 mFgServiceShown = shown; 423 } 424 425 /** 426 * @hide 427 */ 428 @TestApi setDeleted(boolean deleted)429 public void setDeleted(boolean deleted) { 430 mDeleted = deleted; 431 } 432 433 /** 434 * @hide 435 */ 436 @TestApi setDeletedTimeMs(long time)437 public void setDeletedTimeMs(long time) { 438 mDeletedTime = time; 439 } 440 441 /** 442 * @hide 443 */ 444 @TestApi setImportantConversation(boolean importantConvo)445 public void setImportantConversation(boolean importantConvo) { 446 mImportantConvo = importantConvo; 447 } 448 449 /** 450 * Allows users to block notifications sent through this channel, if this channel belongs to 451 * a package that otherwise would have notifications "fixed" as enabled. 452 * 453 * If the channel does not belong to a package that has a fixed notification permission, this 454 * method does nothing, since such channels are blockable by default and cannot be set to be 455 * unblockable. 456 * @param blockable if {@code true}, allows users to block notifications on this channel. 457 */ setBlockable(boolean blockable)458 public void setBlockable(boolean blockable) { 459 mBlockableSystem = blockable; 460 } 461 // Modifiable by apps post channel creation 462 463 /** 464 * Sets the user visible name of this channel. 465 * 466 * <p>The recommended maximum length is 40 characters; the value may be truncated if it is too 467 * long. 468 */ setName(CharSequence name)469 public void setName(CharSequence name) { 470 mName = name != null ? getTrimmedString(name.toString()) : null; 471 } 472 473 /** 474 * Sets the user visible description of this channel. 475 * 476 * <p>The recommended maximum length is 300 characters; the value may be truncated if it is too 477 * long. 478 */ setDescription(String description)479 public void setDescription(String description) { 480 mDesc = getTrimmedString(description); 481 } 482 getTrimmedString(String input)483 private String getTrimmedString(String input) { 484 if (input != null && input.length() > MAX_TEXT_LENGTH) { 485 return input.substring(0, MAX_TEXT_LENGTH); 486 } 487 return input; 488 } 489 490 /** 491 * @hide 492 */ setId(String id)493 public void setId(String id) { 494 mId = id; 495 } 496 497 // Modifiable by apps on channel creation. 498 499 /** 500 * Sets what group this channel belongs to. 501 * 502 * Group information is only used for presentation, not for behavior. 503 * 504 * Only modifiable before the channel is submitted to 505 * {@link NotificationManager#createNotificationChannel(NotificationChannel)}, unless the 506 * channel is not currently part of a group. 507 * 508 * @param groupId the id of a group created by 509 * {@link NotificationManager#createNotificationChannelGroup(NotificationChannelGroup)}. 510 */ setGroup(String groupId)511 public void setGroup(String groupId) { 512 this.mGroup = groupId; 513 } 514 515 /** 516 * Sets whether notifications posted to this channel can appear as application icon badges 517 * in a Launcher. 518 * 519 * Only modifiable before the channel is submitted to 520 * {@link NotificationManager#createNotificationChannel(NotificationChannel)}. 521 * 522 * @param showBadge true if badges should be allowed to be shown. 523 */ setShowBadge(boolean showBadge)524 public void setShowBadge(boolean showBadge) { 525 this.mShowBadge = showBadge; 526 } 527 528 /** 529 * Sets the sound that should be played for notifications posted to this channel and its 530 * audio attributes. Notification channels with an {@link #getImportance() importance} of at 531 * least {@link NotificationManager#IMPORTANCE_DEFAULT} should have a sound. 532 * 533 * Only modifiable before the channel is submitted to 534 * {@link NotificationManager#createNotificationChannel(NotificationChannel)}. 535 */ setSound(Uri sound, AudioAttributes audioAttributes)536 public void setSound(Uri sound, AudioAttributes audioAttributes) { 537 this.mSound = sound; 538 this.mAudioAttributes = audioAttributes; 539 } 540 541 /** 542 * Sets whether notifications posted to this channel should display notification lights, 543 * on devices that support that feature. 544 * 545 * Only modifiable before the channel is submitted to 546 * {@link NotificationManager#createNotificationChannel(NotificationChannel)}. 547 */ enableLights(boolean lights)548 public void enableLights(boolean lights) { 549 this.mLights = lights; 550 } 551 552 /** 553 * Sets the notification light color for notifications posted to this channel, if lights are 554 * {@link #enableLights(boolean) enabled} on this channel and the device supports that feature. 555 * 556 * Only modifiable before the channel is submitted to 557 * {@link NotificationManager#createNotificationChannel(NotificationChannel)}. 558 */ setLightColor(int argb)559 public void setLightColor(int argb) { 560 this.mLightColor = argb; 561 } 562 563 /** 564 * Sets whether notification posted to this channel should vibrate. The vibration pattern can 565 * be set with {@link #setVibrationPattern(long[])}. 566 * 567 * Only modifiable before the channel is submitted to 568 * {@link NotificationManager#createNotificationChannel(NotificationChannel)}. 569 */ enableVibration(boolean vibration)570 public void enableVibration(boolean vibration) { 571 this.mVibrationEnabled = vibration; 572 } 573 574 /** 575 * Sets the vibration pattern for notifications posted to this channel. If the provided 576 * pattern is valid (non-null, non-empty), will {@link #enableVibration(boolean)} enable 577 * vibration} as well. Otherwise, vibration will be disabled. 578 * 579 * Only modifiable before the channel is submitted to 580 * {@link NotificationManager#createNotificationChannel(NotificationChannel)}. 581 */ setVibrationPattern(long[] vibrationPattern)582 public void setVibrationPattern(long[] vibrationPattern) { 583 this.mVibrationEnabled = vibrationPattern != null && vibrationPattern.length > 0; 584 this.mVibration = vibrationPattern; 585 } 586 587 /** 588 * Sets the level of interruption of this notification channel. 589 * 590 * Only modifiable before the channel is submitted to 591 * {@link NotificationManager#createNotificationChannel(NotificationChannel)}. 592 * 593 * @param importance the amount the user should be interrupted by 594 * notifications from this channel. 595 */ setImportance(@mportance int importance)596 public void setImportance(@Importance int importance) { 597 this.mImportance = importance; 598 } 599 600 // Modifiable by a notification ranker. 601 602 /** 603 * Sets whether or not notifications posted to this channel can interrupt the user in 604 * {@link android.app.NotificationManager.Policy#INTERRUPTION_FILTER_PRIORITY} mode. 605 * 606 * Only modifiable by the system and notification ranker. 607 */ setBypassDnd(boolean bypassDnd)608 public void setBypassDnd(boolean bypassDnd) { 609 this.mBypassDnd = bypassDnd; 610 } 611 612 /** 613 * Sets whether notifications posted to this channel appear on the lockscreen or not, and if so, 614 * whether they appear in a redacted form. See e.g. {@link Notification#VISIBILITY_SECRET}. 615 * 616 * Only modifiable by the system and notification ranker. 617 */ setLockscreenVisibility(int lockscreenVisibility)618 public void setLockscreenVisibility(int lockscreenVisibility) { 619 this.mLockscreenVisibility = lockscreenVisibility; 620 } 621 622 /** 623 * As of Android 11 this value is no longer respected. 624 * @see #canBubble() 625 * @see Notification#getBubbleMetadata() 626 */ setAllowBubbles(boolean allowBubbles)627 public void setAllowBubbles(boolean allowBubbles) { 628 mAllowBubbles = allowBubbles ? ALLOW_BUBBLE_ON : ALLOW_BUBBLE_OFF; 629 } 630 631 /** 632 * @hide 633 */ setAllowBubbles(int allowed)634 public void setAllowBubbles(int allowed) { 635 mAllowBubbles = allowed; 636 } 637 638 /** 639 * Sets this channel as being converastion-centric. Different settings and functionality may be 640 * exposed for conversation-centric channels. 641 * 642 * @param parentChannelId The {@link #getId()} id} of the generic channel that notifications of 643 * this type would be posted to in absence of a specific conversation id. 644 * For example, if this channel represents 'Messages from Person A', the 645 * parent channel would be 'Messages.' 646 * @param conversationId The {@link ShortcutInfo#getId()} of the shortcut representing this 647 * channel's conversation. 648 */ setConversationId(@onNull String parentChannelId, @NonNull String conversationId)649 public void setConversationId(@NonNull String parentChannelId, 650 @NonNull String conversationId) { 651 mParentId = parentChannelId; 652 mConversationId = conversationId; 653 } 654 655 /** 656 * Returns the id of this channel. 657 */ getId()658 public String getId() { 659 return mId; 660 } 661 662 /** 663 * Returns the user visible name of this channel. 664 */ getName()665 public CharSequence getName() { 666 return mName; 667 } 668 669 /** 670 * Returns the user visible description of this channel. 671 */ getDescription()672 public String getDescription() { 673 return mDesc; 674 } 675 676 /** 677 * Returns the user specified importance e.g. {@link NotificationManager#IMPORTANCE_LOW} for 678 * notifications posted to this channel. Note: This value might be > 679 * {@link NotificationManager#IMPORTANCE_NONE}, but notifications posted to this channel will 680 * not be shown to the user if the parent {@link NotificationChannelGroup} or app is blocked. 681 * See {@link NotificationChannelGroup#isBlocked()} and 682 * {@link NotificationManager#areNotificationsEnabled()}. 683 */ getImportance()684 public int getImportance() { 685 return mImportance; 686 } 687 688 /** 689 * Whether or not notifications posted to this channel can bypass the Do Not Disturb 690 * {@link NotificationManager#INTERRUPTION_FILTER_PRIORITY} mode. 691 */ canBypassDnd()692 public boolean canBypassDnd() { 693 return mBypassDnd; 694 } 695 696 /** 697 * Whether or not this channel represents a conversation. 698 */ isConversation()699 public boolean isConversation() { 700 return !TextUtils.isEmpty(getConversationId()); 701 } 702 703 704 /** 705 * Whether or not notifications in this conversation are considered important. 706 * 707 * <p>Important conversations may get special visual treatment, and might be able to bypass DND. 708 * 709 * <p>This is only valid for channels that represent conversations, that is, 710 * where {@link #isConversation()} is true. 711 */ isImportantConversation()712 public boolean isImportantConversation() { 713 return mImportantConvo; 714 } 715 716 /** 717 * Returns the notification sound for this channel. 718 */ getSound()719 public Uri getSound() { 720 return mSound; 721 } 722 723 /** 724 * Returns the audio attributes for sound played by notifications posted to this channel. 725 */ getAudioAttributes()726 public AudioAttributes getAudioAttributes() { 727 return mAudioAttributes; 728 } 729 730 /** 731 * Returns whether notifications posted to this channel trigger notification lights. 732 */ shouldShowLights()733 public boolean shouldShowLights() { 734 return mLights; 735 } 736 737 /** 738 * Returns the notification light color for notifications posted to this channel. Irrelevant 739 * unless {@link #shouldShowLights()}. 740 */ getLightColor()741 public int getLightColor() { 742 return mLightColor; 743 } 744 745 /** 746 * Returns whether notifications posted to this channel always vibrate. 747 */ shouldVibrate()748 public boolean shouldVibrate() { 749 return mVibrationEnabled; 750 } 751 752 /** 753 * Returns the vibration pattern for notifications posted to this channel. Will be ignored if 754 * vibration is not enabled ({@link #shouldVibrate()}. 755 */ getVibrationPattern()756 public long[] getVibrationPattern() { 757 return mVibration; 758 } 759 760 /** 761 * Returns whether or not notifications posted to this channel are shown on the lockscreen in 762 * full or redacted form. 763 */ getLockscreenVisibility()764 public int getLockscreenVisibility() { 765 return mLockscreenVisibility; 766 } 767 768 /** 769 * Returns whether notifications posted to this channel can appear as badges in a Launcher 770 * application. 771 * 772 * Note that badging may be disabled for other reasons. 773 */ canShowBadge()774 public boolean canShowBadge() { 775 return mShowBadge; 776 } 777 778 /** 779 * Returns what group this channel belongs to. 780 * 781 * This is used only for visually grouping channels in the UI. 782 */ getGroup()783 public String getGroup() { 784 return mGroup; 785 } 786 787 /** 788 * Returns whether notifications posted to this channel are allowed to display outside of the 789 * notification shade, in a floating window on top of other apps. 790 * 791 * @see Notification#getBubbleMetadata() 792 */ canBubble()793 public boolean canBubble() { 794 return mAllowBubbles == ALLOW_BUBBLE_ON; 795 } 796 797 /** 798 * @hide 799 */ getAllowBubbles()800 public int getAllowBubbles() { 801 return mAllowBubbles; 802 } 803 804 /** 805 * Returns the {@link #getId() id} of the parent notification channel to this channel, if it's 806 * a conversation related channel. See {@link #setConversationId(String, String)}. 807 */ getParentChannelId()808 public @Nullable String getParentChannelId() { 809 return mParentId; 810 } 811 812 /** 813 * Returns the {@link ShortcutInfo#getId() id} of the conversation backing this channel, if it's 814 * associated with a conversation. See {@link #setConversationId(String, String)}. 815 */ getConversationId()816 public @Nullable String getConversationId() { 817 return mConversationId; 818 } 819 820 /** 821 * @hide 822 */ 823 @SystemApi isDeleted()824 public boolean isDeleted() { 825 return mDeleted; 826 } 827 828 /** 829 * @hide 830 */ getDeletedTimeMs()831 public long getDeletedTimeMs() { 832 return mDeletedTime; 833 } 834 835 /** 836 * @hide 837 */ 838 @SystemApi getUserLockedFields()839 public int getUserLockedFields() { 840 return mUserLockedFields; 841 } 842 843 /** 844 * @hide 845 */ isFgServiceShown()846 public boolean isFgServiceShown() { 847 return mFgServiceShown; 848 } 849 850 /** 851 * Returns whether this channel is always blockable, even if the app is 'fixed' as 852 * non-blockable. 853 */ isBlockable()854 public boolean isBlockable() { 855 return mBlockableSystem; 856 } 857 858 /** 859 * @hide 860 */ 861 @TestApi setImportanceLockedByCriticalDeviceFunction(boolean locked)862 public void setImportanceLockedByCriticalDeviceFunction(boolean locked) { 863 mImportanceLockedDefaultApp = locked; 864 } 865 866 /** 867 * @hide 868 */ 869 @TestApi isImportanceLockedByCriticalDeviceFunction()870 public boolean isImportanceLockedByCriticalDeviceFunction() { 871 return mImportanceLockedDefaultApp; 872 } 873 874 /** 875 * @hide 876 */ 877 @TestApi getOriginalImportance()878 public int getOriginalImportance() { 879 return mOriginalImportance; 880 } 881 882 /** 883 * @hide 884 */ 885 @TestApi setOriginalImportance(int importance)886 public void setOriginalImportance(int importance) { 887 mOriginalImportance = importance; 888 } 889 890 /** 891 * @hide 892 */ 893 @TestApi setDemoted(boolean demoted)894 public void setDemoted(boolean demoted) { 895 mDemoted = demoted; 896 } 897 898 /** 899 * Returns whether the user has decided that this channel does not represent a conversation. The 900 * value will always be false for channels that never claimed to be conversations - that is, 901 * for channels where {@link #getConversationId()} and {@link #getParentChannelId()} are empty. 902 */ isDemoted()903 public boolean isDemoted() { 904 return mDemoted; 905 } 906 907 /** 908 * Returns whether the user has chosen the importance of this channel, either to affirm the 909 * initial selection from the app, or changed it to be higher or lower. 910 * @see #getImportance() 911 */ hasUserSetImportance()912 public boolean hasUserSetImportance() { 913 return (mUserLockedFields & USER_LOCKED_IMPORTANCE) != 0; 914 } 915 916 /** 917 * Returns whether the user has chosen the sound of this channel. 918 * @see #getSound() 919 */ hasUserSetSound()920 public boolean hasUserSetSound() { 921 return (mUserLockedFields & USER_LOCKED_SOUND) != 0; 922 } 923 924 /** 925 * @hide 926 */ populateFromXmlForRestore(XmlPullParser parser, Context context)927 public void populateFromXmlForRestore(XmlPullParser parser, Context context) { 928 populateFromXml(XmlUtils.makeTyped(parser), true, context); 929 } 930 931 /** 932 * @hide 933 */ 934 @SystemApi populateFromXml(XmlPullParser parser)935 public void populateFromXml(XmlPullParser parser) { 936 populateFromXml(XmlUtils.makeTyped(parser), false, null); 937 } 938 939 /** 940 * If {@param forRestore} is true, {@param Context} MUST be non-null. 941 */ populateFromXml(TypedXmlPullParser parser, boolean forRestore, @Nullable Context context)942 private void populateFromXml(TypedXmlPullParser parser, boolean forRestore, 943 @Nullable Context context) { 944 Preconditions.checkArgument(!forRestore || context != null, 945 "forRestore is true but got null context"); 946 947 // Name, id, and importance are set in the constructor. 948 setDescription(parser.getAttributeValue(null, ATT_DESC)); 949 setBypassDnd(Notification.PRIORITY_DEFAULT 950 != safeInt(parser, ATT_PRIORITY, Notification.PRIORITY_DEFAULT)); 951 setLockscreenVisibility(safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY)); 952 953 Uri sound = safeUri(parser, ATT_SOUND); 954 setSound(forRestore ? restoreSoundUri(context, sound) : sound, safeAudioAttributes(parser)); 955 956 enableLights(safeBool(parser, ATT_LIGHTS, false)); 957 setLightColor(safeInt(parser, ATT_LIGHT_COLOR, DEFAULT_LIGHT_COLOR)); 958 setVibrationPattern(safeLongArray(parser, ATT_VIBRATION, null)); 959 enableVibration(safeBool(parser, ATT_VIBRATION_ENABLED, false)); 960 setShowBadge(safeBool(parser, ATT_SHOW_BADGE, false)); 961 setDeleted(safeBool(parser, ATT_DELETED, false)); 962 setDeletedTimeMs(XmlUtils.readLongAttribute( 963 parser, ATT_DELETED_TIME_MS, DEFAULT_DELETION_TIME_MS)); 964 setGroup(parser.getAttributeValue(null, ATT_GROUP)); 965 lockFields(safeInt(parser, ATT_USER_LOCKED, 0)); 966 setFgServiceShown(safeBool(parser, ATT_FG_SERVICE_SHOWN, false)); 967 setBlockable(safeBool(parser, ATT_BLOCKABLE_SYSTEM, false)); 968 setAllowBubbles(safeInt(parser, ATT_ALLOW_BUBBLE, DEFAULT_ALLOW_BUBBLE)); 969 setOriginalImportance(safeInt(parser, ATT_ORIG_IMP, DEFAULT_IMPORTANCE)); 970 setConversationId(parser.getAttributeValue(null, ATT_PARENT_CHANNEL), 971 parser.getAttributeValue(null, ATT_CONVERSATION_ID)); 972 setDemoted(safeBool(parser, ATT_DEMOTE, false)); 973 setImportantConversation(safeBool(parser, ATT_IMP_CONVERSATION, false)); 974 } 975 976 @Nullable restoreSoundUri(Context context, @Nullable Uri uri)977 private Uri restoreSoundUri(Context context, @Nullable Uri uri) { 978 if (uri == null || Uri.EMPTY.equals(uri)) { 979 return null; 980 } 981 ContentResolver contentResolver = context.getContentResolver(); 982 // There are backups out there with uncanonical uris (because we fixed this after 983 // shipping). If uncanonical uris are given to MediaProvider.uncanonicalize it won't 984 // verify the uri against device storage and we'll possibly end up with a broken uri. 985 // We then canonicalize the uri to uncanonicalize it back, which means we properly check 986 // the uri and in the case of not having the resource we end up with the default - better 987 // than broken. As a side effect we'll canonicalize already canonicalized uris, this is fine 988 // according to the docs because canonicalize method has to handle canonical uris as well. 989 Uri canonicalizedUri = contentResolver.canonicalize(uri); 990 if (canonicalizedUri == null) { 991 // We got a null because the uri in the backup does not exist here, so we return default 992 return Settings.System.DEFAULT_NOTIFICATION_URI; 993 } 994 return contentResolver.uncanonicalize(canonicalizedUri); 995 } 996 997 /** 998 * @hide 999 */ 1000 @SystemApi writeXml(XmlSerializer out)1001 public void writeXml(XmlSerializer out) throws IOException { 1002 writeXml(XmlUtils.makeTyped(out), false, null); 1003 } 1004 1005 /** 1006 * @hide 1007 */ writeXmlForBackup(XmlSerializer out, Context context)1008 public void writeXmlForBackup(XmlSerializer out, Context context) throws IOException { 1009 writeXml(XmlUtils.makeTyped(out), true, context); 1010 } 1011 getSoundForBackup(Context context)1012 private Uri getSoundForBackup(Context context) { 1013 Uri sound = getSound(); 1014 if (sound == null || Uri.EMPTY.equals(sound)) { 1015 return null; 1016 } 1017 Uri canonicalSound = context.getContentResolver().canonicalize(sound); 1018 if (canonicalSound == null) { 1019 // The content provider does not support canonical uris so we backup the default 1020 return Settings.System.DEFAULT_NOTIFICATION_URI; 1021 } 1022 return canonicalSound; 1023 } 1024 1025 /** 1026 * If {@param forBackup} is true, {@param Context} MUST be non-null. 1027 */ writeXml(TypedXmlSerializer out, boolean forBackup, @Nullable Context context)1028 private void writeXml(TypedXmlSerializer out, boolean forBackup, @Nullable Context context) 1029 throws IOException { 1030 Preconditions.checkArgument(!forBackup || context != null, 1031 "forBackup is true but got null context"); 1032 out.startTag(null, TAG_CHANNEL); 1033 out.attribute(null, ATT_ID, getId()); 1034 if (getName() != null) { 1035 out.attribute(null, ATT_NAME, getName().toString()); 1036 } 1037 if (getDescription() != null) { 1038 out.attribute(null, ATT_DESC, getDescription()); 1039 } 1040 if (getImportance() != DEFAULT_IMPORTANCE) { 1041 out.attributeInt(null, ATT_IMPORTANCE, getImportance()); 1042 } 1043 if (canBypassDnd()) { 1044 out.attributeInt(null, ATT_PRIORITY, Notification.PRIORITY_MAX); 1045 } 1046 if (getLockscreenVisibility() != DEFAULT_VISIBILITY) { 1047 out.attributeInt(null, ATT_VISIBILITY, getLockscreenVisibility()); 1048 } 1049 Uri sound = forBackup ? getSoundForBackup(context) : getSound(); 1050 if (sound != null) { 1051 out.attribute(null, ATT_SOUND, sound.toString()); 1052 } 1053 if (getAudioAttributes() != null) { 1054 out.attributeInt(null, ATT_USAGE, getAudioAttributes().getUsage()); 1055 out.attributeInt(null, ATT_CONTENT_TYPE, getAudioAttributes().getContentType()); 1056 out.attributeInt(null, ATT_FLAGS, getAudioAttributes().getFlags()); 1057 } 1058 if (shouldShowLights()) { 1059 out.attributeBoolean(null, ATT_LIGHTS, shouldShowLights()); 1060 } 1061 if (getLightColor() != DEFAULT_LIGHT_COLOR) { 1062 out.attributeInt(null, ATT_LIGHT_COLOR, getLightColor()); 1063 } 1064 if (shouldVibrate()) { 1065 out.attributeBoolean(null, ATT_VIBRATION_ENABLED, shouldVibrate()); 1066 } 1067 if (getVibrationPattern() != null) { 1068 out.attribute(null, ATT_VIBRATION, longArrayToString(getVibrationPattern())); 1069 } 1070 if (getUserLockedFields() != 0) { 1071 out.attributeInt(null, ATT_USER_LOCKED, getUserLockedFields()); 1072 } 1073 if (isFgServiceShown()) { 1074 out.attributeBoolean(null, ATT_FG_SERVICE_SHOWN, isFgServiceShown()); 1075 } 1076 if (canShowBadge()) { 1077 out.attributeBoolean(null, ATT_SHOW_BADGE, canShowBadge()); 1078 } 1079 if (isDeleted()) { 1080 out.attributeBoolean(null, ATT_DELETED, isDeleted()); 1081 } 1082 if (getDeletedTimeMs() >= 0) { 1083 out.attributeLong(null, ATT_DELETED_TIME_MS, getDeletedTimeMs()); 1084 } 1085 if (getGroup() != null) { 1086 out.attribute(null, ATT_GROUP, getGroup()); 1087 } 1088 if (isBlockable()) { 1089 out.attributeBoolean(null, ATT_BLOCKABLE_SYSTEM, isBlockable()); 1090 } 1091 if (getAllowBubbles() != DEFAULT_ALLOW_BUBBLE) { 1092 out.attributeInt(null, ATT_ALLOW_BUBBLE, getAllowBubbles()); 1093 } 1094 if (getOriginalImportance() != DEFAULT_IMPORTANCE) { 1095 out.attributeInt(null, ATT_ORIG_IMP, getOriginalImportance()); 1096 } 1097 if (getParentChannelId() != null) { 1098 out.attribute(null, ATT_PARENT_CHANNEL, getParentChannelId()); 1099 } 1100 if (getConversationId() != null) { 1101 out.attribute(null, ATT_CONVERSATION_ID, getConversationId()); 1102 } 1103 if (isDemoted()) { 1104 out.attributeBoolean(null, ATT_DEMOTE, isDemoted()); 1105 } 1106 if (isImportantConversation()) { 1107 out.attributeBoolean(null, ATT_IMP_CONVERSATION, isImportantConversation()); 1108 } 1109 1110 // mImportanceLockedDefaultApp has a different source of truth and so isn't written to 1111 // this xml file 1112 1113 out.endTag(null, TAG_CHANNEL); 1114 } 1115 1116 /** 1117 * @hide 1118 */ 1119 @SystemApi toJson()1120 public JSONObject toJson() throws JSONException { 1121 JSONObject record = new JSONObject(); 1122 record.put(ATT_ID, getId()); 1123 record.put(ATT_NAME, getName()); 1124 record.put(ATT_DESC, getDescription()); 1125 if (getImportance() != DEFAULT_IMPORTANCE) { 1126 record.put(ATT_IMPORTANCE, 1127 NotificationListenerService.Ranking.importanceToString(getImportance())); 1128 } 1129 if (canBypassDnd()) { 1130 record.put(ATT_PRIORITY, Notification.PRIORITY_MAX); 1131 } 1132 if (getLockscreenVisibility() != DEFAULT_VISIBILITY) { 1133 record.put(ATT_VISIBILITY, Notification.visibilityToString(getLockscreenVisibility())); 1134 } 1135 if (getSound() != null) { 1136 record.put(ATT_SOUND, getSound().toString()); 1137 } 1138 if (getAudioAttributes() != null) { 1139 record.put(ATT_USAGE, Integer.toString(getAudioAttributes().getUsage())); 1140 record.put(ATT_CONTENT_TYPE, 1141 Integer.toString(getAudioAttributes().getContentType())); 1142 record.put(ATT_FLAGS, Integer.toString(getAudioAttributes().getFlags())); 1143 } 1144 record.put(ATT_LIGHTS, Boolean.toString(shouldShowLights())); 1145 record.put(ATT_LIGHT_COLOR, Integer.toString(getLightColor())); 1146 record.put(ATT_VIBRATION_ENABLED, Boolean.toString(shouldVibrate())); 1147 record.put(ATT_USER_LOCKED, Integer.toString(getUserLockedFields())); 1148 record.put(ATT_FG_SERVICE_SHOWN, Boolean.toString(isFgServiceShown())); 1149 record.put(ATT_VIBRATION, longArrayToString(getVibrationPattern())); 1150 record.put(ATT_SHOW_BADGE, Boolean.toString(canShowBadge())); 1151 record.put(ATT_DELETED, Boolean.toString(isDeleted())); 1152 record.put(ATT_DELETED_TIME_MS, Long.toString(getDeletedTimeMs())); 1153 record.put(ATT_GROUP, getGroup()); 1154 record.put(ATT_BLOCKABLE_SYSTEM, isBlockable()); 1155 record.put(ATT_ALLOW_BUBBLE, getAllowBubbles()); 1156 // TODO: original importance 1157 return record; 1158 } 1159 safeAudioAttributes(TypedXmlPullParser parser)1160 private static AudioAttributes safeAudioAttributes(TypedXmlPullParser parser) { 1161 int usage = safeInt(parser, ATT_USAGE, AudioAttributes.USAGE_NOTIFICATION); 1162 int contentType = safeInt(parser, ATT_CONTENT_TYPE, 1163 AudioAttributes.CONTENT_TYPE_SONIFICATION); 1164 int flags = safeInt(parser, ATT_FLAGS, 0); 1165 return new AudioAttributes.Builder() 1166 .setUsage(usage) 1167 .setContentType(contentType) 1168 .setFlags(flags) 1169 .build(); 1170 } 1171 safeUri(TypedXmlPullParser parser, String att)1172 private static Uri safeUri(TypedXmlPullParser parser, String att) { 1173 final String val = parser.getAttributeValue(null, att); 1174 return val == null ? null : Uri.parse(val); 1175 } 1176 safeInt(TypedXmlPullParser parser, String att, int defValue)1177 private static int safeInt(TypedXmlPullParser parser, String att, int defValue) { 1178 return parser.getAttributeInt(null, att, defValue); 1179 } 1180 safeBool(TypedXmlPullParser parser, String att, boolean defValue)1181 private static boolean safeBool(TypedXmlPullParser parser, String att, boolean defValue) { 1182 return parser.getAttributeBoolean(null, att, defValue); 1183 } 1184 safeLongArray(TypedXmlPullParser parser, String att, long[] defValue)1185 private static long[] safeLongArray(TypedXmlPullParser parser, String att, long[] defValue) { 1186 final String attributeValue = parser.getAttributeValue(null, att); 1187 if (TextUtils.isEmpty(attributeValue)) return defValue; 1188 String[] values = attributeValue.split(DELIMITER); 1189 long[] longValues = new long[values.length]; 1190 for (int i = 0; i < values.length; i++) { 1191 try { 1192 longValues[i] = Long.parseLong(values[i]); 1193 } catch (NumberFormatException e) { 1194 longValues[i] = 0; 1195 } 1196 } 1197 return longValues; 1198 } 1199 longArrayToString(long[] values)1200 private static String longArrayToString(long[] values) { 1201 StringBuilder sb = new StringBuilder(); 1202 if (values != null && values.length > 0) { 1203 for (int i = 0; i < values.length - 1; i++) { 1204 sb.append(values[i]).append(DELIMITER); 1205 } 1206 sb.append(values[values.length - 1]); 1207 } 1208 return sb.toString(); 1209 } 1210 1211 public static final @android.annotation.NonNull Creator<NotificationChannel> CREATOR = 1212 new Creator<NotificationChannel>() { 1213 @Override 1214 public NotificationChannel createFromParcel(Parcel in) { 1215 return new NotificationChannel(in); 1216 } 1217 1218 @Override 1219 public NotificationChannel[] newArray(int size) { 1220 return new NotificationChannel[size]; 1221 } 1222 }; 1223 1224 @Override describeContents()1225 public int describeContents() { 1226 return 0; 1227 } 1228 1229 @Override equals(@ullable Object o)1230 public boolean equals(@Nullable Object o) { 1231 if (this == o) return true; 1232 if (o == null || getClass() != o.getClass()) return false; 1233 NotificationChannel that = (NotificationChannel) o; 1234 return getImportance() == that.getImportance() 1235 && mBypassDnd == that.mBypassDnd 1236 && getLockscreenVisibility() == that.getLockscreenVisibility() 1237 && mLights == that.mLights 1238 && getLightColor() == that.getLightColor() 1239 && getUserLockedFields() == that.getUserLockedFields() 1240 && isFgServiceShown() == that.isFgServiceShown() 1241 && mVibrationEnabled == that.mVibrationEnabled 1242 && mShowBadge == that.mShowBadge 1243 && isDeleted() == that.isDeleted() 1244 && getDeletedTimeMs() == that.getDeletedTimeMs() 1245 && isBlockable() == that.isBlockable() 1246 && mAllowBubbles == that.mAllowBubbles 1247 && Objects.equals(getId(), that.getId()) 1248 && Objects.equals(getName(), that.getName()) 1249 && Objects.equals(mDesc, that.mDesc) 1250 && Objects.equals(getSound(), that.getSound()) 1251 && Arrays.equals(mVibration, that.mVibration) 1252 && Objects.equals(getGroup(), that.getGroup()) 1253 && Objects.equals(getAudioAttributes(), that.getAudioAttributes()) 1254 && mImportanceLockedDefaultApp == that.mImportanceLockedDefaultApp 1255 && mOriginalImportance == that.mOriginalImportance 1256 && Objects.equals(getParentChannelId(), that.getParentChannelId()) 1257 && Objects.equals(getConversationId(), that.getConversationId()) 1258 && isDemoted() == that.isDemoted() 1259 && isImportantConversation() == that.isImportantConversation(); 1260 } 1261 1262 @Override hashCode()1263 public int hashCode() { 1264 int result = Objects.hash(getId(), getName(), mDesc, getImportance(), mBypassDnd, 1265 getLockscreenVisibility(), getSound(), mLights, getLightColor(), 1266 getUserLockedFields(), 1267 isFgServiceShown(), mVibrationEnabled, mShowBadge, isDeleted(), getDeletedTimeMs(), 1268 getGroup(), getAudioAttributes(), isBlockable(), mAllowBubbles, 1269 mImportanceLockedDefaultApp, mOriginalImportance, 1270 mParentId, mConversationId, mDemoted, mImportantConvo); 1271 result = 31 * result + Arrays.hashCode(mVibration); 1272 return result; 1273 } 1274 1275 /** @hide */ dump(PrintWriter pw, String prefix, boolean redacted)1276 public void dump(PrintWriter pw, String prefix, boolean redacted) { 1277 String redactedName = redacted ? TextUtils.trimToLengthWithEllipsis(mName, 3) : mName; 1278 String output = "NotificationChannel{" 1279 + "mId='" + mId + '\'' 1280 + ", mName=" + redactedName 1281 + getFieldsString() 1282 + '}'; 1283 pw.println(prefix + output); 1284 } 1285 1286 @Override toString()1287 public String toString() { 1288 return "NotificationChannel{" 1289 + "mId='" + mId + '\'' 1290 + ", mName=" + mName 1291 + getFieldsString() 1292 + '}'; 1293 } 1294 getFieldsString()1295 private String getFieldsString() { 1296 return ", mDescription=" + (!TextUtils.isEmpty(mDesc) ? "hasDescription " : "") 1297 + ", mImportance=" + mImportance 1298 + ", mBypassDnd=" + mBypassDnd 1299 + ", mLockscreenVisibility=" + mLockscreenVisibility 1300 + ", mSound=" + mSound 1301 + ", mLights=" + mLights 1302 + ", mLightColor=" + mLightColor 1303 + ", mVibration=" + Arrays.toString(mVibration) 1304 + ", mUserLockedFields=" + Integer.toHexString(mUserLockedFields) 1305 + ", mFgServiceShown=" + mFgServiceShown 1306 + ", mVibrationEnabled=" + mVibrationEnabled 1307 + ", mShowBadge=" + mShowBadge 1308 + ", mDeleted=" + mDeleted 1309 + ", mDeletedTimeMs=" + mDeletedTime 1310 + ", mGroup='" + mGroup + '\'' 1311 + ", mAudioAttributes=" + mAudioAttributes 1312 + ", mBlockableSystem=" + mBlockableSystem 1313 + ", mAllowBubbles=" + mAllowBubbles 1314 + ", mImportanceLockedDefaultApp=" + mImportanceLockedDefaultApp 1315 + ", mOriginalImp=" + mOriginalImportance 1316 + ", mParent=" + mParentId 1317 + ", mConversationId=" + mConversationId 1318 + ", mDemoted=" + mDemoted 1319 + ", mImportantConvo=" + mImportantConvo; 1320 } 1321 1322 /** @hide */ dumpDebug(ProtoOutputStream proto, long fieldId)1323 public void dumpDebug(ProtoOutputStream proto, long fieldId) { 1324 final long token = proto.start(fieldId); 1325 1326 proto.write(NotificationChannelProto.ID, mId); 1327 proto.write(NotificationChannelProto.NAME, mName); 1328 proto.write(NotificationChannelProto.DESCRIPTION, mDesc); 1329 proto.write(NotificationChannelProto.IMPORTANCE, mImportance); 1330 proto.write(NotificationChannelProto.CAN_BYPASS_DND, mBypassDnd); 1331 proto.write(NotificationChannelProto.LOCKSCREEN_VISIBILITY, mLockscreenVisibility); 1332 if (mSound != null) { 1333 proto.write(NotificationChannelProto.SOUND, mSound.toString()); 1334 } 1335 proto.write(NotificationChannelProto.USE_LIGHTS, mLights); 1336 proto.write(NotificationChannelProto.LIGHT_COLOR, mLightColor); 1337 if (mVibration != null) { 1338 for (long v : mVibration) { 1339 proto.write(NotificationChannelProto.VIBRATION, v); 1340 } 1341 } 1342 proto.write(NotificationChannelProto.USER_LOCKED_FIELDS, mUserLockedFields); 1343 proto.write(NotificationChannelProto.FG_SERVICE_SHOWN, mFgServiceShown); 1344 proto.write(NotificationChannelProto.IS_VIBRATION_ENABLED, mVibrationEnabled); 1345 proto.write(NotificationChannelProto.SHOW_BADGE, mShowBadge); 1346 proto.write(NotificationChannelProto.IS_DELETED, mDeleted); 1347 proto.write(NotificationChannelProto.GROUP, mGroup); 1348 if (mAudioAttributes != null) { 1349 mAudioAttributes.dumpDebug(proto, NotificationChannelProto.AUDIO_ATTRIBUTES); 1350 } 1351 proto.write(NotificationChannelProto.IS_BLOCKABLE_SYSTEM, mBlockableSystem); 1352 proto.write(NotificationChannelProto.ALLOW_APP_OVERLAY, mAllowBubbles); 1353 1354 proto.end(token); 1355 } 1356 } 1357