NotificationChannel.java revision 924eed1ca6d3fec5dae7eb0f9c11b8f23f628697
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 */ 16package android.app; 17 18import org.json.JSONException; 19import org.json.JSONObject; 20import org.xmlpull.v1.XmlPullParser; 21import org.xmlpull.v1.XmlSerializer; 22 23import android.annotation.SystemApi; 24import android.net.Uri; 25import android.os.Parcel; 26import android.os.Parcelable; 27import android.service.notification.NotificationListenerService; 28import android.text.TextUtils; 29 30import java.io.IOException; 31import java.util.Arrays; 32 33/** 34 * A representation of settings that apply to a collection of similarly themed notifications. 35 */ 36public final class NotificationChannel implements Parcelable { 37 38 /** 39 * The id of the default channel for an app. All notifications posted without a notification 40 * channel specified are posted to this channel. 41 */ 42 public static final String DEFAULT_CHANNEL_ID = "miscellaneous"; 43 44 private static final String TAG_CHANNEL = "channel"; 45 private static final String ATT_NAME = "name"; 46 private static final String ATT_ID = "id"; 47 private static final String ATT_DELETED = "deleted"; 48 private static final String ATT_PRIORITY = "priority"; 49 private static final String ATT_VISIBILITY = "visibility"; 50 private static final String ATT_IMPORTANCE = "importance"; 51 private static final String ATT_LIGHTS = "lights"; 52 private static final String ATT_VIBRATION = "vibration"; 53 private static final String ATT_VIBRATION_ENABLED = "vibration_enabled"; 54 private static final String ATT_SOUND = "sound"; 55 //TODO: add audio attributes support 56 private static final String ATT_AUDIO_ATTRIBUTES = "audio_attributes"; 57 private static final String ATT_SHOW_BADGE = "show_badge"; 58 private static final String ATT_USER_LOCKED = "locked"; 59 private static final String DELIMITER = ","; 60 61 /** 62 * @hide 63 */ 64 @SystemApi 65 public static final int USER_LOCKED_PRIORITY = 0x00000001; 66 /** 67 * @hide 68 */ 69 @SystemApi 70 public static final int USER_LOCKED_VISIBILITY = 0x00000002; 71 /** 72 * @hide 73 */ 74 @SystemApi 75 public static final int USER_LOCKED_IMPORTANCE = 0x00000004; 76 /** 77 * @hide 78 */ 79 @SystemApi 80 public static final int USER_LOCKED_LIGHTS = 0x00000008; 81 /** 82 * @hide 83 */ 84 @SystemApi 85 public static final int USER_LOCKED_VIBRATION = 0x00000010; 86 /** 87 * @hide 88 */ 89 @SystemApi 90 public static final int USER_LOCKED_SOUND = 0x00000020; 91 92 /** 93 * @hide 94 */ 95 @SystemApi 96 public static final int USER_LOCKED_ALLOWED = 0x00000040; 97 98 /** 99 * @hide 100 */ 101 @SystemApi 102 public static final int USER_LOCKED_SHOW_BADGE = 0x00000080; 103 104 /** 105 * @hide 106 */ 107 @SystemApi 108 public static final int[] LOCKABLE_FIELDS = new int[] { 109 USER_LOCKED_PRIORITY, 110 USER_LOCKED_VISIBILITY, 111 USER_LOCKED_IMPORTANCE, 112 USER_LOCKED_LIGHTS, 113 USER_LOCKED_VIBRATION, 114 USER_LOCKED_SOUND, 115 USER_LOCKED_ALLOWED, 116 USER_LOCKED_SHOW_BADGE 117 }; 118 119 120 private static final int DEFAULT_VISIBILITY = 121 NotificationManager.VISIBILITY_NO_OVERRIDE; 122 private static final int DEFAULT_IMPORTANCE = 123 NotificationManager.IMPORTANCE_UNSPECIFIED; 124 private static final boolean DEFAULT_DELETED = false; 125 private static final boolean DEFAULT_SHOW_BADGE = true; 126 127 private final String mId; 128 private CharSequence mName; 129 private int mImportance = DEFAULT_IMPORTANCE; 130 private boolean mBypassDnd; 131 private int mLockscreenVisibility = DEFAULT_VISIBILITY; 132 private Uri mSound; 133 private boolean mLights; 134 private long[] mVibration; 135 private int mUserLockedFields; 136 private boolean mVibrationEnabled; 137 private boolean mShowBadge = DEFAULT_SHOW_BADGE; 138 private boolean mDeleted = DEFAULT_DELETED; 139 140 /** 141 * Creates a notification channel. 142 * 143 * @param id The id of the channel. Must be unique per package. 144 * @param name The user visible name of the channel. 145 * @param importance The importance of the channel. This controls how interruptive notifications 146 * posted to this channel are. See e.g. 147 * {@link NotificationManager#IMPORTANCE_DEFAULT}. 148 */ 149 public NotificationChannel(String id, CharSequence name, int importance) { 150 this.mId = id; 151 this.mName = name; 152 this.mImportance = importance; 153 } 154 155 protected NotificationChannel(Parcel in) { 156 if (in.readByte() != 0) { 157 mId = in.readString(); 158 } else { 159 mId = null; 160 } 161 mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); 162 mImportance = in.readInt(); 163 mBypassDnd = in.readByte() != 0; 164 mLockscreenVisibility = in.readInt(); 165 if (in.readByte() != 0) { 166 mSound = Uri.CREATOR.createFromParcel(in); 167 } else { 168 mSound = null; 169 } 170 mLights = in.readByte() != 0; 171 mVibration = in.createLongArray(); 172 mUserLockedFields = in.readInt(); 173 mVibrationEnabled = in.readByte() != 0; 174 mShowBadge = in.readByte() != 0; 175 mDeleted = in.readByte() != 0; 176 } 177 178 @Override 179 public void writeToParcel(Parcel dest, int flags) { 180 if (mId != null) { 181 dest.writeByte((byte) 1); 182 dest.writeString(mId); 183 } else { 184 dest.writeByte((byte) 0); 185 } 186 TextUtils.writeToParcel(mName, dest, flags); 187 dest.writeInt(mImportance); 188 dest.writeByte(mBypassDnd ? (byte) 1 : (byte) 0); 189 dest.writeInt(mLockscreenVisibility); 190 if (mSound != null) { 191 dest.writeByte((byte) 1); 192 mSound.writeToParcel(dest, 0); 193 } else { 194 dest.writeByte((byte) 0); 195 } 196 dest.writeByte(mLights ? (byte) 1 : (byte) 0); 197 dest.writeLongArray(mVibration); 198 dest.writeInt(mUserLockedFields); 199 dest.writeByte(mVibrationEnabled ? (byte) 1 : (byte) 0); 200 dest.writeByte(mShowBadge ? (byte) 1 : (byte) 0); 201 dest.writeByte(mDeleted ? (byte) 1 : (byte) 0); 202 } 203 204 /** 205 * @hide 206 */ 207 @SystemApi 208 public void lockFields(int field) { 209 mUserLockedFields |= field; 210 } 211 212 /** 213 * @hide 214 */ 215 @SystemApi 216 public void setDeleted(boolean deleted) { 217 mDeleted = deleted; 218 } 219 220 // Modifiable by a notification ranker. 221 222 /** 223 * Sets whether or not notifications posted to this channel can interrupt the user in 224 * {@link android.app.NotificationManager.Policy#INTERRUPTION_FILTER_PRIORITY} mode. 225 * 226 * Only modifiable by the system and notification ranker. 227 */ 228 public void setBypassDnd(boolean bypassDnd) { 229 this.mBypassDnd = bypassDnd; 230 } 231 232 /** 233 * Sets whether notifications posted to this channel appear on the lockscreen or not, and if so, 234 * whether they appear in a redacted form. See e.g. {@link Notification#VISIBILITY_SECRET}. 235 * 236 * Only modifiable by the system and notification ranker. 237 */ 238 public void setLockscreenVisibility(int lockscreenVisibility) { 239 this.mLockscreenVisibility = lockscreenVisibility; 240 } 241 242 /** 243 * Sets the level of interruption of this notification channel. 244 * 245 * Only modifiable by the system and notification ranker. 246 * 247 * @param importance the amount the user should be interrupted by notifications from this 248 * channel. See e.g. 249 * {@link android.app.NotificationManager#IMPORTANCE_DEFAULT}. 250 */ 251 public void setImportance(int importance) { 252 this.mImportance = importance; 253 } 254 255 // Modifiable by apps on channel creation. 256 257 /** 258 * Sets whether notifications posted to this channel can appear as application icon badges 259 * in a Launcher. 260 * 261 * @param showBadge true if badges should be allowed to be shown. 262 */ 263 public void setShowBadge(boolean showBadge) { 264 this.mShowBadge = showBadge; 265 } 266 267 /** 268 * Sets the sound that should be played for notifications posted to this channel if 269 * the notifications don't supply a sound. Only modifiable before the channel is submitted 270 * to the NotificationManager. 271 */ 272 public void setSound(Uri sound) { 273 this.mSound = sound; 274 } 275 276 /** 277 * Sets whether notifications posted to this channel should display notification lights, 278 * on devices that support that feature. Only modifiable before the channel is submitted to 279 * the NotificationManager. 280 */ 281 public void setLights(boolean lights) { 282 this.mLights = lights; 283 } 284 285 /** 286 * Sets whether notification posted to this channel should vibrate. The vibration pattern can 287 * be set with {@link #setVibrationPattern(long[])}. Only modifiable before the channel is 288 * submitted to the NotificationManager. 289 */ 290 public void enableVibration(boolean vibration) { 291 this.mVibrationEnabled = vibration; 292 } 293 294 /** 295 * Sets whether notification posted to this channel should vibrate. Only modifiable before the 296 * channel is submitted to the NotificationManager. 297 */ 298 public void setVibrationPattern(long[] vibrationPattern) { 299 this.mVibration = vibrationPattern; 300 } 301 302 /** 303 * Returns the id of this channel. 304 */ 305 public String getId() { 306 return mId; 307 } 308 309 /** 310 * Returns the user visible name of this channel. 311 */ 312 public CharSequence getName() { 313 return mName; 314 } 315 316 /** 317 * Returns the user specified importance {e.g. @link NotificationManager#IMPORTANCE_LOW} for 318 * notifications posted to this channel. 319 */ 320 public int getImportance() { 321 return mImportance; 322 } 323 324 /** 325 * Whether or not notifications posted to this channel can bypass the Do Not Disturb 326 * {@link NotificationManager#INTERRUPTION_FILTER_PRIORITY} mode. 327 */ 328 public boolean canBypassDnd() { 329 return mBypassDnd; 330 } 331 332 /** 333 * Returns the notification sound for this channel. 334 */ 335 public Uri getSound() { 336 return mSound; 337 } 338 339 /** 340 * Returns whether notifications posted to this channel trigger notification lights. 341 */ 342 public boolean shouldShowLights() { 343 return mLights; 344 } 345 346 /** 347 * Returns whether notifications posted to this channel always vibrate. 348 */ 349 public boolean shouldVibrate() { 350 return mVibrationEnabled; 351 } 352 353 /** 354 * Returns the vibration pattern for notifications posted to this channel. Will be ignored if 355 * vibration is not enabled ({@link #shouldVibrate()}. 356 */ 357 public long[] getVibrationPattern() { 358 return mVibration; 359 } 360 361 /** 362 * Returns whether or not notifications posted to this channel are shown on the lockscreen in 363 * full or redacted form. 364 */ 365 public int getLockscreenVisibility() { 366 return mLockscreenVisibility; 367 } 368 369 /** 370 * Returns whether notifications posted to this channel can appear as badges in a Launcher 371 * application. 372 * 373 * Note that badging may be disabled for other reasons. 374 */ 375 public boolean canShowBadge() { 376 return mShowBadge; 377 } 378 379 /** 380 * @hide 381 */ 382 @SystemApi 383 public boolean isDeleted() { 384 return mDeleted; 385 } 386 387 /** 388 * @hide 389 */ 390 @SystemApi 391 public int getUserLockedFields() { 392 return mUserLockedFields; 393 } 394 395 /** 396 * @hide 397 */ 398 @SystemApi 399 public void populateFromXml(XmlPullParser parser) { 400 // Name, id, and importance are set in the constructor. 401 setBypassDnd(Notification.PRIORITY_DEFAULT 402 != safeInt(parser, ATT_PRIORITY, Notification.PRIORITY_DEFAULT)); 403 setLockscreenVisibility(safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY)); 404 setSound(safeUri(parser, ATT_SOUND)); 405 setLights(safeBool(parser, ATT_LIGHTS, false)); 406 enableVibration(safeBool(parser, ATT_VIBRATION_ENABLED, false)); 407 setVibrationPattern(safeLongArray(parser, ATT_VIBRATION, null)); 408 setShowBadge(safeBool(parser, ATT_SHOW_BADGE, false)); 409 setDeleted(safeBool(parser, ATT_DELETED, false)); 410 lockFields(safeInt(parser, ATT_USER_LOCKED, 0)); 411 } 412 413 /** 414 * @hide 415 */ 416 @SystemApi 417 public void writeXml(XmlSerializer out) throws IOException { 418 out.startTag(null, TAG_CHANNEL); 419 out.attribute(null, ATT_ID, getId()); 420 out.attribute(null, ATT_NAME, getName().toString()); 421 if (getImportance() != DEFAULT_IMPORTANCE) { 422 out.attribute( 423 null, ATT_IMPORTANCE, Integer.toString(getImportance())); 424 } 425 if (canBypassDnd()) { 426 out.attribute( 427 null, ATT_PRIORITY, Integer.toString(Notification.PRIORITY_MAX)); 428 } 429 if (getLockscreenVisibility() != DEFAULT_VISIBILITY) { 430 out.attribute(null, ATT_VISIBILITY, 431 Integer.toString(getLockscreenVisibility())); 432 } 433 if (getSound() != null) { 434 out.attribute(null, ATT_SOUND, getSound().toString()); 435 } 436 if (shouldShowLights()) { 437 out.attribute(null, ATT_LIGHTS, Boolean.toString(shouldShowLights())); 438 } 439 if (shouldVibrate()) { 440 out.attribute(null, ATT_VIBRATION_ENABLED, Boolean.toString(shouldVibrate())); 441 } 442 if (getVibrationPattern() != null) { 443 out.attribute(null, ATT_VIBRATION, longArrayToString(getVibrationPattern())); 444 } 445 if (getUserLockedFields() != 0) { 446 out.attribute(null, ATT_USER_LOCKED, Integer.toString(getUserLockedFields())); 447 } 448 if (canShowBadge()) { 449 out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(canShowBadge())); 450 } 451 if (isDeleted()) { 452 out.attribute(null, ATT_DELETED, Boolean.toString(isDeleted())); 453 } 454 455 out.endTag(null, TAG_CHANNEL); 456 } 457 458 /** 459 * @hide 460 */ 461 @SystemApi 462 public JSONObject toJson() throws JSONException { 463 JSONObject record = new JSONObject(); 464 record.put(ATT_ID, getId()); 465 record.put(ATT_NAME, getName()); 466 if (getImportance() != DEFAULT_IMPORTANCE) { 467 record.put(ATT_IMPORTANCE, 468 NotificationListenerService.Ranking.importanceToString(getImportance())); 469 } 470 if (canBypassDnd()) { 471 record.put(ATT_PRIORITY, Notification.PRIORITY_MAX); 472 } 473 if (getLockscreenVisibility() != DEFAULT_VISIBILITY) { 474 record.put(ATT_VISIBILITY, Notification.visibilityToString(getLockscreenVisibility())); 475 } 476 if (getSound() != null) { 477 record.put(ATT_SOUND, getSound().toString()); 478 } 479 record.put(ATT_LIGHTS, Boolean.toString(shouldShowLights())); 480 record.put(ATT_VIBRATION_ENABLED, Boolean.toString(shouldVibrate())); 481 record.put(ATT_USER_LOCKED, Integer.toString(getUserLockedFields())); 482 record.put(ATT_VIBRATION, longArrayToString(getVibrationPattern())); 483 record.put(ATT_SHOW_BADGE, Boolean.toString(canShowBadge())); 484 record.put(ATT_DELETED, Boolean.toString(isDeleted())); 485 return record; 486 } 487 488 private static Uri safeUri(XmlPullParser parser, String att) { 489 final String val = parser.getAttributeValue(null, att); 490 return val == null ? null : Uri.parse(val); 491 } 492 493 private static int safeInt(XmlPullParser parser, String att, int defValue) { 494 final String val = parser.getAttributeValue(null, att); 495 return tryParseInt(val, defValue); 496 } 497 498 private static int tryParseInt(String value, int defValue) { 499 if (TextUtils.isEmpty(value)) return defValue; 500 try { 501 return Integer.parseInt(value); 502 } catch (NumberFormatException e) { 503 return defValue; 504 } 505 } 506 507 private static boolean safeBool(XmlPullParser parser, String att, boolean defValue) { 508 final String value = parser.getAttributeValue(null, att); 509 if (TextUtils.isEmpty(value)) return defValue; 510 return Boolean.parseBoolean(value); 511 } 512 513 private static long[] safeLongArray(XmlPullParser parser, String att, long[] defValue) { 514 final String attributeValue = parser.getAttributeValue(null, att); 515 if (TextUtils.isEmpty(attributeValue)) return defValue; 516 String[] values = attributeValue.split(DELIMITER); 517 long[] longValues = new long[values.length]; 518 for (int i = 0; i < values.length; i++) { 519 try { 520 longValues[i] = Long.parseLong(values[i]); 521 } catch (NumberFormatException e) { 522 longValues[i] = 0; 523 } 524 } 525 return longValues; 526 } 527 528 private static String longArrayToString(long[] values) { 529 StringBuffer sb = new StringBuffer(); 530 for (int i = 0; i < values.length - 1; i++) { 531 sb.append(values[i]).append(DELIMITER); 532 } 533 sb.append(values[values.length - 1]); 534 return sb.toString(); 535 } 536 537 public static final Creator<NotificationChannel> CREATOR = new Creator<NotificationChannel>() { 538 @Override 539 public NotificationChannel createFromParcel(Parcel in) { 540 return new NotificationChannel(in); 541 } 542 543 @Override 544 public NotificationChannel[] newArray(int size) { 545 return new NotificationChannel[size]; 546 } 547 }; 548 549 @Override 550 public int describeContents() { 551 return 0; 552 } 553 554 @Override 555 public boolean equals(Object o) { 556 if (this == o) return true; 557 if (o == null || getClass() != o.getClass()) return false; 558 559 NotificationChannel that = (NotificationChannel) o; 560 561 if (mImportance != that.mImportance) return false; 562 if (mBypassDnd != that.mBypassDnd) return false; 563 if (mLockscreenVisibility != that.mLockscreenVisibility) return false; 564 if (mLights != that.mLights) return false; 565 if (mUserLockedFields != that.mUserLockedFields) return false; 566 if (mVibrationEnabled != that.mVibrationEnabled) return false; 567 if (mShowBadge != that.mShowBadge) return false; 568 if (mDeleted != that.mDeleted) return false; 569 if (mId != null ? !mId.equals(that.mId) : that.mId != null) return false; 570 if (mName != null ? !mName.equals(that.mName) : that.mName != null) return false; 571 if (mSound != null ? !mSound.equals(that.mSound) : that.mSound != null) return false; 572 return Arrays.equals(mVibration, that.mVibration); 573 574 } 575 576 @Override 577 public int hashCode() { 578 int result = mId != null ? mId.hashCode() : 0; 579 result = 31 * result + (mName != null ? mName.hashCode() : 0); 580 result = 31 * result + mImportance; 581 result = 31 * result + (mBypassDnd ? 1 : 0); 582 result = 31 * result + mLockscreenVisibility; 583 result = 31 * result + (mSound != null ? mSound.hashCode() : 0); 584 result = 31 * result + (mLights ? 1 : 0); 585 result = 31 * result + Arrays.hashCode(mVibration); 586 result = 31 * result + mUserLockedFields; 587 result = 31 * result + (mVibrationEnabled ? 1 : 0); 588 result = 31 * result + (mShowBadge ? 1 : 0); 589 result = 31 * result + (mDeleted ? 1 : 0); 590 return result; 591 } 592 593 @Override 594 public String toString() { 595 return "NotificationChannel{" + 596 "mId='" + mId + '\'' + 597 ", mName=" + mName + 598 ", mImportance=" + mImportance + 599 ", mBypassDnd=" + mBypassDnd + 600 ", mLockscreenVisibility=" + mLockscreenVisibility + 601 ", mSound=" + mSound + 602 ", mLights=" + mLights + 603 ", mVibration=" + Arrays.toString(mVibration) + 604 ", mUserLockedFields=" + mUserLockedFields + 605 ", mVibrationEnabled=" + mVibrationEnabled + 606 ", mShowBadge=" + mShowBadge + 607 ", mDeleted=" + mDeleted + 608 '}'; 609 } 610} 611