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