NotificationChannelGroup.java revision 8d2b053611fe4a52602c125bf1f577c12083848a
1/*
2 * Copyright (C) 2017 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 android.annotation.SystemApi;
19import android.annotation.TestApi;
20import android.content.Intent;
21import android.os.Parcel;
22import android.os.Parcelable;
23import android.text.TextUtils;
24import android.util.proto.ProtoOutputStream;
25
26import org.json.JSONException;
27import org.json.JSONObject;
28import org.xmlpull.v1.XmlPullParser;
29import org.xmlpull.v1.XmlSerializer;
30
31import java.io.IOException;
32import java.util.ArrayList;
33import java.util.List;
34
35/**
36 * A grouping of related notification channels. e.g., channels that all belong to a single account.
37 */
38public final class NotificationChannelGroup implements Parcelable {
39
40    /**
41     * The maximum length for text fields in a NotificationChannelGroup. Fields will be truncated at
42     * this limit.
43     */
44    private static final int MAX_TEXT_LENGTH = 1000;
45
46    private static final String TAG_GROUP = "channelGroup";
47    private static final String ATT_NAME = "name";
48    private static final String ATT_DESC = "desc";
49    private static final String ATT_ID = "id";
50    private static final String ATT_BLOCKED = "blocked";
51
52    private final String mId;
53    private CharSequence mName;
54    private String mDescription;
55    private boolean mBlocked;
56    private List<NotificationChannel> mChannels = new ArrayList<>();
57
58    /**
59     * Creates a notification channel group.
60     *
61     * @param id The id of the group. Must be unique per package.  the value may be truncated if
62     *           it is too long.
63     * @param name The user visible name of the group. You can rename this group when the system
64     *             locale changes by listening for the {@link Intent#ACTION_LOCALE_CHANGED}
65     *             broadcast. <p>The recommended maximum length is 40 characters; the value may be
66     *             truncated if it is too long.
67     */
68    public NotificationChannelGroup(String id, CharSequence name) {
69        this.mId = getTrimmedString(id);
70        this.mName = name != null ? getTrimmedString(name.toString()) : null;
71    }
72
73    /**
74     * @hide
75     */
76    protected NotificationChannelGroup(Parcel in) {
77        if (in.readByte() != 0) {
78            mId = in.readString();
79        } else {
80            mId = null;
81        }
82        mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
83        if (in.readByte() != 0) {
84            mDescription = in.readString();
85        } else {
86            mDescription = null;
87        }
88        in.readParcelableList(mChannels, NotificationChannel.class.getClassLoader());
89        mBlocked = in.readBoolean();
90    }
91
92    private String getTrimmedString(String input) {
93        if (input != null && input.length() > MAX_TEXT_LENGTH) {
94            return input.substring(0, MAX_TEXT_LENGTH);
95        }
96        return input;
97    }
98
99    @Override
100    public void writeToParcel(Parcel dest, int flags) {
101        if (mId != null) {
102            dest.writeByte((byte) 1);
103            dest.writeString(mId);
104        } else {
105            dest.writeByte((byte) 0);
106        }
107        TextUtils.writeToParcel(mName, dest, flags);
108        if (mDescription != null) {
109            dest.writeByte((byte) 1);
110            dest.writeString(mDescription);
111        } else {
112            dest.writeByte((byte) 0);
113        }
114        dest.writeParcelableList(mChannels, flags);
115        dest.writeBoolean(mBlocked);
116    }
117
118    /**
119     * Returns the id of this group.
120     */
121    public String getId() {
122        return mId;
123    }
124
125    /**
126     * Returns the user visible name of this group.
127     */
128    public CharSequence getName() {
129        return mName;
130    }
131
132    /**
133     * Returns the user visible description of this group.
134     */
135    public String getDescription() {
136        return mDescription;
137    }
138
139    /**
140     * Returns the list of channels that belong to this group
141     */
142    public List<NotificationChannel> getChannels() {
143        return mChannels;
144    }
145
146    /**
147     * Returns whether or not notifications posted to {@link NotificationChannel channels} belonging
148     * to this group are blocked. This value is independent of
149     * {@link NotificationManager#areNotificationsEnabled()} and
150     * {@link NotificationChannel#getImportance()}.
151     */
152    public boolean isBlocked() {
153        return mBlocked;
154    }
155
156    /**
157     * Sets the user visible description of this group.
158     *
159     * <p>The recommended maximum length is 300 characters; the value may be truncated if it is too
160     * long.
161     */
162    public void setDescription(String description) {
163        mDescription = getTrimmedString(description);
164    }
165
166    /**
167     * @hide
168     */
169    @TestApi
170    public void setBlocked(boolean blocked) {
171        mBlocked = blocked;
172    }
173
174    /**
175     * @hide
176     */
177    public void addChannel(NotificationChannel channel) {
178        mChannels.add(channel);
179    }
180
181    /**
182     * @hide
183     */
184    public void setChannels(List<NotificationChannel> channels) {
185        mChannels = channels;
186    }
187
188    /**
189     * @hide
190     */
191    public void populateFromXml(XmlPullParser parser) {
192        // Name, id, and importance are set in the constructor.
193        setDescription(parser.getAttributeValue(null, ATT_DESC));
194        setBlocked(safeBool(parser, ATT_BLOCKED, false));
195    }
196
197    private static boolean safeBool(XmlPullParser parser, String att, boolean defValue) {
198        final String value = parser.getAttributeValue(null, att);
199        if (TextUtils.isEmpty(value)) return defValue;
200        return Boolean.parseBoolean(value);
201    }
202
203    /**
204     * @hide
205     */
206    public void writeXml(XmlSerializer out) throws IOException {
207        out.startTag(null, TAG_GROUP);
208
209        out.attribute(null, ATT_ID, getId());
210        if (getName() != null) {
211            out.attribute(null, ATT_NAME, getName().toString());
212        }
213        if (getDescription() != null) {
214            out.attribute(null, ATT_DESC, getDescription().toString());
215        }
216        out.attribute(null, ATT_BLOCKED, Boolean.toString(isBlocked()));
217
218        out.endTag(null, TAG_GROUP);
219    }
220
221    /**
222     * @hide
223     */
224    @SystemApi
225    public JSONObject toJson() throws JSONException {
226        JSONObject record = new JSONObject();
227        record.put(ATT_ID, getId());
228        record.put(ATT_NAME, getName());
229        record.put(ATT_DESC, getDescription());
230        record.put(ATT_BLOCKED, isBlocked());
231        return record;
232    }
233
234    public static final Creator<NotificationChannelGroup> CREATOR =
235            new Creator<NotificationChannelGroup>() {
236        @Override
237        public NotificationChannelGroup createFromParcel(Parcel in) {
238            return new NotificationChannelGroup(in);
239        }
240
241        @Override
242        public NotificationChannelGroup[] newArray(int size) {
243            return new NotificationChannelGroup[size];
244        }
245    };
246
247    @Override
248    public int describeContents() {
249        return 0;
250    }
251
252    @Override
253    public boolean equals(Object o) {
254        if (this == o) return true;
255        if (o == null || getClass() != o.getClass()) return false;
256
257        NotificationChannelGroup that = (NotificationChannelGroup) o;
258
259        if (isBlocked() != that.isBlocked()) return false;
260        if (getId() != null ? !getId().equals(that.getId()) : that.getId() != null) return false;
261        if (getName() != null ? !getName().equals(that.getName()) : that.getName() != null) {
262            return false;
263        }
264        if (getDescription() != null ? !getDescription().equals(that.getDescription())
265                : that.getDescription() != null) {
266            return false;
267        }
268        return getChannels() != null ? getChannels().equals(that.getChannels())
269                : that.getChannels() == null;
270    }
271
272    @Override
273    public int hashCode() {
274        int result = getId() != null ? getId().hashCode() : 0;
275        result = 31 * result + (getName() != null ? getName().hashCode() : 0);
276        result = 31 * result + (getDescription() != null ? getDescription().hashCode() : 0);
277        result = 31 * result + (isBlocked() ? 1 : 0);
278        result = 31 * result + (getChannels() != null ? getChannels().hashCode() : 0);
279        return result;
280    }
281
282    @Override
283    public NotificationChannelGroup clone() {
284        NotificationChannelGroup cloned = new NotificationChannelGroup(getId(), getName());
285        cloned.setDescription(getDescription());
286        cloned.setBlocked(isBlocked());
287        cloned.setChannels(getChannels());
288        return cloned;
289    }
290
291    @Override
292    public String toString() {
293        return "NotificationChannelGroup{"
294                + "mId='" + mId + '\''
295                + ", mName=" + mName
296                + ", mDescription=" + (!TextUtils.isEmpty(mDescription) ? "hasDescription " : "")
297                + ", mBlocked=" + mBlocked
298                + ", mChannels=" + mChannels
299                + '}';
300    }
301
302    /** @hide */
303    public void writeToProto(ProtoOutputStream proto, long fieldId) {
304        final long token = proto.start(fieldId);
305
306        proto.write(NotificationChannelGroupProto.ID, mId);
307        proto.write(NotificationChannelGroupProto.NAME, mName.toString());
308        proto.write(NotificationChannelGroupProto.DESCRIPTION, mDescription);
309        proto.write(NotificationChannelGroupProto.IS_BLOCKED, mBlocked);
310        for (NotificationChannel channel : mChannels) {
311            channel.writeToProto(proto, NotificationChannelGroupProto.CHANNELS);
312        }
313
314        proto.end(token);
315    }
316}
317