diff options
| author | Julia Reynolds <juliacr@google.com> | 2016-08-16 15:00:25 -0400 |
|---|---|---|
| committer | Julia Reynolds <juliacr@google.com> | 2016-09-15 08:42:35 -0400 |
| commit | b5e44b796618c376cf535e43aaa86ea4522e7770 (patch) | |
| tree | e68121690f5cc9baccad2b1d37fd037b8fc92a93 /core/java/android/app/NotificationChannel.java | |
| parent | 3cece22080fea16eccf41b62a5222774b194545f (diff) | |
Add Notification channels
In this iteration:
-Every app gets a default channel that notifications will be posted
to if they don't specify a channel themselves. The default channel
inherits app-wide settings on upgrade.
-Apps can create new channels without user approval, but apps
cannot change the name of a channel once created, nor can they ever
set the importance.
- When a notification is posted:
- If the channel is marked as 'vibrates', vibration will be
applied to notifications that lack a vibration. unlike the default
notification flag, notifications will retain their custom vibration
if given
- Same with sound and lights
- A notification's importance is the min of the app and channel
importance
- A notification can bypass dnd if: either the app or channel settings
say it can
- A notification's show on lockscreen setting comes from the app first,
and the channel second if the app has no preference
Tests: in cl. also there's a cl for cts and a test app.
Change-Id: I630f99df655800b00586dcfab538d320d04fe0f0
Diffstat (limited to 'core/java/android/app/NotificationChannel.java')
| -rw-r--r-- | core/java/android/app/NotificationChannel.java | 382 |
1 files changed, 382 insertions, 0 deletions
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java new file mode 100644 index 000000000000..530b8bb7f3eb --- /dev/null +++ b/core/java/android/app/NotificationChannel.java @@ -0,0 +1,382 @@ +package android.app; + +import org.json.JSONException; +import org.json.JSONObject; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlSerializer; + +import android.annotation.SystemApi; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; +import android.service.notification.NotificationListenerService; +import android.text.TextUtils; + +import java.io.IOException; + +/** + * A representation of settings that apply to a collection of similarly themed notifications. + */ +public final class NotificationChannel implements Parcelable { + + /** + * The id of the default channel for an app. All notifications posted without a notification + * channel specified are posted to this channel. + */ + public static final String DEFAULT_CHANNEL_ID = "miscellaneous"; + + private static final String TAG_CHANNEL = "channel"; + private static final String ATT_NAME = "name"; + private static final String ATT_ID = "id"; + private static final String ATT_PRIORITY = "priority"; + private static final String ATT_VISIBILITY = "visibility"; + private static final String ATT_IMPORTANCE = "importance"; + private static final String ATT_LIGHTS = "lights"; + private static final String ATT_VIBRATION = "vibration"; + private static final String ATT_DEFAULT_RINGTONE = "ringtone"; + + private static final int DEFAULT_VISIBILITY = + NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE; + private static final int DEFAULT_IMPORTANCE = + NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED; + + private final String mId; + private CharSequence mName; + private int mImportance = DEFAULT_IMPORTANCE; + private boolean mBypassDnd; + private int mLockscreenVisibility = DEFAULT_VISIBILITY; + private Uri mRingtone; + private boolean mLights; + private boolean mVibration; + + /** + * Creates a notification channel. + * + * @param id The id of the channel. Must be unique per package. + * @param name The user visible name of the channel. + */ + public NotificationChannel(String id, CharSequence name) { + this.mId = id; + this.mName = name; + } + + protected NotificationChannel(Parcel in) { + if (in.readByte() != 0) { + mId = in.readString(); + } else { + mId = null; + } + mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + mImportance = in.readInt(); + mBypassDnd = in.readByte() != 0; + mLockscreenVisibility = in.readInt(); + if (in.readByte() != 0) { + mRingtone = Uri.CREATOR.createFromParcel(in); + } else { + mRingtone = null; + } + mLights = in.readByte() != 0; + mVibration = in.readByte() != 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + if (mId != null) { + dest.writeByte((byte) 1); + dest.writeString(mId); + } else { + dest.writeByte((byte) 0); + } + TextUtils.writeToParcel(mName, dest, flags); + dest.writeInt(mImportance); + dest.writeByte(mBypassDnd ? (byte) 1 : (byte) 0); + dest.writeInt(mLockscreenVisibility); + if (mRingtone != null) { + dest.writeByte((byte) 1); + mRingtone.writeToParcel(dest, 0); + } else { + dest.writeByte((byte) 0); + } + dest.writeByte(mLights ? (byte) 1 : (byte) 0); + dest.writeByte(mVibration ? (byte) 1 : (byte) 0); + } + + // Only modifiable by users. + /** + * @hide + */ + @SystemApi + public void setName(CharSequence name) { + this.mName = name; + } + + /** + * @hide + */ + @SystemApi + public void setImportance(int importance) { + this.mImportance = importance; + } + + /** + * @hide + */ + @SystemApi + public void setBypassDnd(boolean bypassDnd) { + this.mBypassDnd = bypassDnd; + } + + /** + * @hide + */ + @SystemApi + public void setLockscreenVisibility(int lockscreenVisibility) { + this.mLockscreenVisibility = lockscreenVisibility; + } + + // Modifiable by apps. + + /** + * Sets the ringtone that should be played for notifications posted to this channel if + * the notifications don't supply a ringtone. + */ + public void setDefaultRingtone(Uri defaultRingtone) { + this.mRingtone = defaultRingtone; + } + + /** + * Sets whether notifications posted to this channel should display notification lights, + * on devices that support that feature. + */ + public void setLights(boolean lights) { + this.mLights = lights; + } + + /** + * Sets whether notification posted to this channel should vibrate, even if individual + * notifications are marked as having vibration. + */ + public void setVibration(boolean vibration) { + this.mVibration = vibration; + } + + /** + * Returns the id of this channel. + */ + public String getId() { + return mId; + } + + /** + * Returns the user visible name of this channel. + */ + public CharSequence getName() { + return mName; + } + + /** + * Returns the user specified importance {e.g. @link NotificationManager#IMPORTANCE_LOW} for + * notifications posted to this channel. + */ + public int getImportance() { + return mImportance; + } + + /** + * Whether or not notifications posted to this channel can bypass the Do Not Disturb + * {@link NotificationManager#INTERRUPTION_FILTER_PRIORITY} mode. + */ + public boolean canBypassDnd() { + return mBypassDnd; + } + + /** + * Returns the notification sound for this channel. + */ + public Uri getDefaultRingtone() { + return mRingtone; + } + + /** + * Returns whether notifications posted to this channel trigger notification lights. + */ + public boolean shouldShowLights() { + return mLights; + } + + /** + * Returns whether notifications posted to this channel always vibrate. + */ + public boolean shouldVibrate() { + return mVibration; + } + + /** + * @hide + */ + @SystemApi + public int getLockscreenVisibility() { + return mLockscreenVisibility; + } + + /** + * @hide + */ + @SystemApi + public void populateFromXml(XmlPullParser parser) { + // Name and id are set in the constructor. + setImportance(safeInt(parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE)); + setBypassDnd(Notification.PRIORITY_DEFAULT + != safeInt(parser, ATT_PRIORITY, Notification.PRIORITY_DEFAULT)); + setLockscreenVisibility(safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY)); + setDefaultRingtone(safeUri(parser, ATT_DEFAULT_RINGTONE)); + setLights(safeBool(parser, ATT_LIGHTS, false)); + setVibration(safeBool(parser, ATT_VIBRATION, false)); + } + + /** + * @hide + */ + @SystemApi + public void writeXml(XmlSerializer out) throws IOException { + out.startTag(null, TAG_CHANNEL); + out.attribute(null, ATT_ID, getId()); + out.attribute(null, ATT_NAME, getName().toString()); + if (getImportance() != DEFAULT_IMPORTANCE) { + out.attribute( + null, ATT_IMPORTANCE, Integer.toString(getImportance())); + } + if (canBypassDnd()) { + out.attribute( + null, ATT_PRIORITY, Integer.toString(Notification.PRIORITY_MAX)); + } + if (getLockscreenVisibility() != DEFAULT_VISIBILITY) { + out.attribute(null, ATT_VISIBILITY, + Integer.toString(getLockscreenVisibility())); + } + if (getDefaultRingtone() != null) { + out.attribute(null, ATT_DEFAULT_RINGTONE, getDefaultRingtone().toString()); + } + if (shouldShowLights()) { + out.attribute(null, ATT_LIGHTS, Boolean.toString(shouldShowLights())); + } + if (shouldVibrate()) { + out.attribute(null, ATT_VIBRATION, Boolean.toString(shouldVibrate())); + } + out.endTag(null, TAG_CHANNEL); + } + + /** + * @hide + */ + @SystemApi + public JSONObject toJson() throws JSONException { + JSONObject record = new JSONObject(); + record.put(ATT_ID, getId()); + record.put(ATT_NAME, getName()); + if (getImportance() != DEFAULT_IMPORTANCE) { + record.put(ATT_IMPORTANCE, + NotificationListenerService.Ranking.importanceToString(getImportance())); + } + if (canBypassDnd()) { + record.put(ATT_PRIORITY, Notification.PRIORITY_MAX); + } + if (getLockscreenVisibility() != DEFAULT_VISIBILITY) { + record.put(ATT_VISIBILITY, Notification.visibilityToString(getLockscreenVisibility())); + } + if (getDefaultRingtone() != null) { + record.put(ATT_DEFAULT_RINGTONE, getDefaultRingtone().toString()); + } + record.put(ATT_LIGHTS, Boolean.toString(shouldShowLights())); + record.put(ATT_VIBRATION, Boolean.toString(shouldVibrate())); + + return record; + } + + private static Uri safeUri(XmlPullParser parser, String att) { + final String val = parser.getAttributeValue(null, att); + return val == null ? null : Uri.parse(val); + } + + private static int safeInt(XmlPullParser parser, String att, int defValue) { + final String val = parser.getAttributeValue(null, att); + return tryParseInt(val, defValue); + } + + private static int tryParseInt(String value, int defValue) { + if (TextUtils.isEmpty(value)) return defValue; + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + return defValue; + } + } + + private static boolean safeBool(XmlPullParser parser, String att, boolean defValue) { + final String value = parser.getAttributeValue(null, att); + if (TextUtils.isEmpty(value)) return defValue; + return Boolean.parseBoolean(value); + } + + public static final Creator<NotificationChannel> CREATOR = new Creator<NotificationChannel>() { + @Override + public NotificationChannel createFromParcel(Parcel in) { + return new NotificationChannel(in); + } + + @Override + public NotificationChannel[] newArray(int size) { + return new NotificationChannel[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + NotificationChannel that = (NotificationChannel) o; + + if (mImportance != that.mImportance) return false; + if (mBypassDnd != that.mBypassDnd) return false; + if (mLockscreenVisibility != that.mLockscreenVisibility) return false; + if (mLights != that.mLights) return false; + if (mVibration != that.mVibration) return false; + if (!mId.equals(that.mId)) return false; + if (mName != null ? !mName.equals(that.mName) : that.mName != null) return false; + return mRingtone != null ? mRingtone.equals( + that.mRingtone) : that.mRingtone == null; + } + + @Override + public String toString() { + return "NotificationChannel{" + + "mId='" + mId + '\'' + + ", mName=" + mName + + ", mImportance=" + mImportance + + ", mBypassDnd=" + mBypassDnd + + ", mLockscreenVisibility=" + mLockscreenVisibility + + ", mRingtone='" + mRingtone + '\'' + + ", mLights=" + mLights + + ", mVibration=" + mVibration + + '}'; + } + + @Override + public int hashCode() { + int result = mId.hashCode(); + result = 31 * result + (mName != null ? mName.hashCode() : 0); + result = 31 * result + mImportance; + result = 31 * result + (mBypassDnd ? 1 : 0); + result = 31 * result + mLockscreenVisibility; + result = 31 * result + (mRingtone != null ? mRingtone.hashCode() : 0); + result = 31 * result + (mLights ? 1 : 0); + result = 31 * result + (mVibration ? 1 : 0); + return result; + } +} |
