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