1 /*
2  * Copyright (C) 2019 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 
17 package com.android.systemui.statusbar.notification.collection;
18 
19 import android.annotation.Nullable;
20 import android.app.Notification;
21 import android.app.NotificationChannel;
22 import android.app.NotificationManager;
23 import android.content.Context;
24 import android.content.pm.ShortcutInfo;
25 import android.os.UserHandle;
26 import android.service.notification.NotificationListenerService.Ranking;
27 import android.service.notification.SnoozeCriterion;
28 import android.service.notification.StatusBarNotification;
29 
30 import androidx.annotation.NonNull;
31 
32 import com.android.internal.logging.InstanceId;
33 import com.android.systemui.statusbar.RankingBuilder;
34 import com.android.systemui.statusbar.SbnBuilder;
35 import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection;
36 import com.android.systemui.util.time.FakeSystemClock;
37 
38 import kotlin.Unit;
39 
40 import java.util.ArrayList;
41 import java.util.function.Consumer;
42 
43 /**
44  * Combined builder for constructing a NotificationEntry and its associated StatusBarNotification
45  * and Ranking. Is largely a proxy for the SBN and Ranking builders, but does a little extra magic
46  * to make sure the keys match between the two, etc.
47  *
48  * Has the ability to set ListEntry properties as well.
49  *
50  * Only for use in tests.
51  */
52 public class NotificationEntryBuilder {
53     private final SbnBuilder mSbnBuilder;
54     private final RankingBuilder mRankingBuilder;
55     private final FakeSystemClock mClock = new FakeSystemClock();
56     private StatusBarNotification mSbn = null;
57 
58     /* ListEntry properties */
59     private GroupEntry mParent;
60     private NotifSection mNotifSection;
61 
62     /* If set, use this creation time instead of mClock.uptimeMillis */
63     private long mCreationTime = -1;
64     private int mStableIndex = -1;
65 
NotificationEntryBuilder()66     public NotificationEntryBuilder() {
67         mSbnBuilder = new SbnBuilder();
68         mRankingBuilder = new RankingBuilder();
69     }
70 
NotificationEntryBuilder(NotificationEntry source)71     public NotificationEntryBuilder(NotificationEntry source) {
72         mSbnBuilder = new SbnBuilder(source.getSbn());
73         mRankingBuilder = new RankingBuilder(source.getRanking());
74 
75         mParent = source.getParent();
76         mCreationTime = source.getCreationTime();
77     }
78 
79     /** Allows the caller to sub-build the ranking */
80     @NonNull
updateRanking(@onNull Consumer<RankingBuilder> rankingUpdater)81     public NotificationEntryBuilder updateRanking(@NonNull Consumer<RankingBuilder> rankingUpdater) {
82         rankingUpdater.accept(mRankingBuilder);
83         return this;
84     }
85 
86     /** Allows the caller to sub-build the SBN */
87     @NonNull
updateSbn(@onNull Consumer<SbnBuilder> sbnUpdater)88     public NotificationEntryBuilder updateSbn(@NonNull Consumer<SbnBuilder> sbnUpdater) {
89         sbnUpdater.accept(mSbnBuilder);
90         return this;
91     }
92 
93     /** Update an the parent on an existing entry */
setNewParent(NotificationEntry entry, GroupEntry parent)94     public static void setNewParent(NotificationEntry entry, GroupEntry parent) {
95         entry.setParent(parent);
96     }
97 
98     /** Build a new instance of NotificationEntry */
build()99     public NotificationEntry build() {
100         return buildOrApply(null);
101     }
102 
103     /** Modifies [target] to match the contents of this builder */
apply(NotificationEntry target)104     public void apply(NotificationEntry target) {
105         buildOrApply(target);
106     }
107 
108     /** Convenience method for Kotlin callbacks that are passed a builder and need to return Unit */
done()109     public Unit done() {
110         return Unit.INSTANCE;
111     }
112 
buildOrApply(NotificationEntry target)113     private NotificationEntry buildOrApply(NotificationEntry target) {
114         final StatusBarNotification sbn = mSbn != null ? mSbn : mSbnBuilder.build();
115         final Ranking ranking = mRankingBuilder.setKey(sbn.getKey()).build();
116         final long creationTime = mCreationTime != -1 ? mCreationTime : mClock.uptimeMillis();
117 
118         final NotificationEntry entry;
119         if (target == null) {
120             entry = new NotificationEntry(sbn, ranking, creationTime);
121         } else {
122             entry = target;
123             entry.setSbn(sbn);
124             entry.setRanking(ranking);
125             // Note: we can't modify the creation time as it's immutable
126         }
127 
128         /* ListEntry properties */
129         entry.setParent(mParent);
130         entry.getAttachState().setSection(mNotifSection);
131         entry.getAttachState().setStableIndex(mStableIndex);
132         return entry;
133     }
134 
135     /**
136      * Sets the parent.
137      */
setParent(@ullable GroupEntry parent)138     public NotificationEntryBuilder setParent(@Nullable GroupEntry parent) {
139         mParent = parent;
140         return this;
141     }
142 
143     /**
144      * Sets the parent.
145      */
setSection(@ullable NotifSection section)146     public NotificationEntryBuilder setSection(@Nullable NotifSection section) {
147         mNotifSection = section;
148         return this;
149     }
150 
151     /**
152      * Sets the SBN directly. If set, causes all calls to delegated SbnBuilder methods to be
153      * ignored.
154      */
setSbn(@ullable StatusBarNotification sbn)155     public NotificationEntryBuilder setSbn(@Nullable StatusBarNotification sbn) {
156         mSbn = sbn;
157         return this;
158     }
159 
setStableIndex(int index)160     public NotificationEntryBuilder setStableIndex(int index) {
161         mStableIndex = index;
162         return this;
163     }
164 
165     /**
166      * Set the creation time
167      */
setCreationTime(long creationTime)168     public NotificationEntryBuilder setCreationTime(long creationTime) {
169         mCreationTime = creationTime;
170         return this;
171     }
172 
173     /* Delegated to SbnBuilder */
174 
setPkg(String pkg)175     public NotificationEntryBuilder setPkg(String pkg) {
176         mSbnBuilder.setPkg(pkg);
177         return this;
178     }
179 
setOpPkg(String opPkg)180     public NotificationEntryBuilder setOpPkg(String opPkg) {
181         mSbnBuilder.setOpPkg(opPkg);
182         return this;
183     }
184 
setId(int id)185     public NotificationEntryBuilder setId(int id) {
186         mSbnBuilder.setId(id);
187         return this;
188     }
189 
setTag(String tag)190     public NotificationEntryBuilder setTag(String tag) {
191         mSbnBuilder.setTag(tag);
192         return this;
193     }
194 
setUid(int uid)195     public NotificationEntryBuilder setUid(int uid) {
196         mSbnBuilder.setUid(uid);
197         return this;
198     }
199 
setInitialPid(int initialPid)200     public NotificationEntryBuilder setInitialPid(int initialPid) {
201         mSbnBuilder.setInitialPid(initialPid);
202         return this;
203     }
204 
setNotification(Notification notification)205     public NotificationEntryBuilder setNotification(Notification notification) {
206         mSbnBuilder.setNotification(notification);
207         return this;
208     }
209 
modifyNotification(Context context)210     public Notification.Builder modifyNotification(Context context) {
211         return mSbnBuilder.modifyNotification(context);
212     }
213 
setUser(UserHandle user)214     public NotificationEntryBuilder setUser(UserHandle user) {
215         mSbnBuilder.setUser(user);
216         return this;
217     }
218 
setOverrideGroupKey(String overrideGroupKey)219     public NotificationEntryBuilder setOverrideGroupKey(String overrideGroupKey) {
220         mSbnBuilder.setOverrideGroupKey(overrideGroupKey);
221         return this;
222     }
223 
setPostTime(long postTime)224     public NotificationEntryBuilder setPostTime(long postTime) {
225         mSbnBuilder.setPostTime(postTime);
226         return this;
227     }
228 
setInstanceId(InstanceId instanceId)229     public NotificationEntryBuilder setInstanceId(InstanceId instanceId) {
230         mSbnBuilder.setInstanceId(instanceId);
231         return this;
232     }
233 
234     /* Delegated to Notification.Builder (via SbnBuilder) */
235 
setContentTitle(Context context, String contentTitle)236     public NotificationEntryBuilder setContentTitle(Context context, String contentTitle) {
237         mSbnBuilder.setContentTitle(context, contentTitle);
238         return this;
239     }
240 
setContentText(Context context, String contentText)241     public NotificationEntryBuilder setContentText(Context context, String contentText) {
242         mSbnBuilder.setContentText(context, contentText);
243         return this;
244     }
245 
setGroup(Context context, String groupKey)246     public NotificationEntryBuilder setGroup(Context context, String groupKey) {
247         mSbnBuilder.setGroup(context, groupKey);
248         return this;
249     }
250 
setGroupSummary(Context context, boolean isGroupSummary)251     public NotificationEntryBuilder setGroupSummary(Context context, boolean isGroupSummary) {
252         mSbnBuilder.setGroupSummary(context, isGroupSummary);
253         return this;
254     }
255 
setFlag(Context context, int mask, boolean value)256     public NotificationEntryBuilder setFlag(Context context, int mask, boolean value) {
257         mSbnBuilder.setFlag(context, mask, value);
258         return this;
259     }
260 
261     /* Delegated to RankingBuilder */
262 
setRank(int rank)263     public NotificationEntryBuilder setRank(int rank) {
264         mRankingBuilder.setRank(rank);
265         return this;
266     }
267 
setMatchesInterruptionFilter( boolean matchesInterruptionFilter)268     public NotificationEntryBuilder setMatchesInterruptionFilter(
269             boolean matchesInterruptionFilter) {
270         mRankingBuilder.setMatchesInterruptionFilter(matchesInterruptionFilter);
271         return this;
272     }
273 
setVisibilityOverride(int visibilityOverride)274     public NotificationEntryBuilder setVisibilityOverride(int visibilityOverride) {
275         mRankingBuilder.setVisibilityOverride(visibilityOverride);
276         return this;
277     }
278 
setSuppressedVisualEffects(int suppressedVisualEffects)279     public NotificationEntryBuilder setSuppressedVisualEffects(int suppressedVisualEffects) {
280         mRankingBuilder.setSuppressedVisualEffects(suppressedVisualEffects);
281         return this;
282     }
283 
setExplanation(CharSequence explanation)284     public NotificationEntryBuilder setExplanation(CharSequence explanation) {
285         mRankingBuilder.setExplanation(explanation);
286         return this;
287     }
288 
setAdditionalPeople(ArrayList<String> additionalPeople)289     public NotificationEntryBuilder setAdditionalPeople(ArrayList<String> additionalPeople) {
290         mRankingBuilder.setAdditionalPeople(additionalPeople);
291         return this;
292     }
293 
setSnoozeCriteria( ArrayList<SnoozeCriterion> snoozeCriteria)294     public NotificationEntryBuilder setSnoozeCriteria(
295             ArrayList<SnoozeCriterion> snoozeCriteria) {
296         mRankingBuilder.setSnoozeCriteria(snoozeCriteria);
297         return this;
298     }
299 
setCanShowBadge(boolean canShowBadge)300     public NotificationEntryBuilder setCanShowBadge(boolean canShowBadge) {
301         mRankingBuilder.setCanShowBadge(canShowBadge);
302         return this;
303     }
304 
setSuspended(boolean suspended)305     public NotificationEntryBuilder setSuspended(boolean suspended) {
306         mRankingBuilder.setSuspended(suspended);
307         return this;
308     }
309 
setLastAudiblyAlertedMs(long lastAudiblyAlertedMs)310     public NotificationEntryBuilder setLastAudiblyAlertedMs(long lastAudiblyAlertedMs) {
311         mRankingBuilder.setLastAudiblyAlertedMs(lastAudiblyAlertedMs);
312         return this;
313     }
314 
setNoisy(boolean noisy)315     public NotificationEntryBuilder setNoisy(boolean noisy) {
316         mRankingBuilder.setNoisy(noisy);
317         return this;
318     }
319 
setCanBubble(boolean canBubble)320     public NotificationEntryBuilder setCanBubble(boolean canBubble) {
321         mRankingBuilder.setCanBubble(canBubble);
322         return this;
323     }
324 
setImportance(@otificationManager.Importance int importance)325     public NotificationEntryBuilder setImportance(@NotificationManager.Importance int importance) {
326         mRankingBuilder.setImportance(importance);
327         return this;
328     }
329 
setUserSentiment(int userSentiment)330     public NotificationEntryBuilder setUserSentiment(int userSentiment) {
331         mRankingBuilder.setUserSentiment(userSentiment);
332         return this;
333     }
334 
setChannel(NotificationChannel channel)335     public NotificationEntryBuilder setChannel(NotificationChannel channel) {
336         mRankingBuilder.setChannel(channel);
337         return this;
338     }
339 
setSmartActions( ArrayList<Notification.Action> smartActions)340     public NotificationEntryBuilder setSmartActions(
341             ArrayList<Notification.Action> smartActions) {
342         mRankingBuilder.setSmartActions(smartActions);
343         return this;
344     }
345 
setSmartActions(Notification.Action... smartActions)346     public NotificationEntryBuilder setSmartActions(Notification.Action... smartActions) {
347         mRankingBuilder.setSmartActions(smartActions);
348         return this;
349     }
350 
setSmartReplies(ArrayList<CharSequence> smartReplies)351     public NotificationEntryBuilder setSmartReplies(ArrayList<CharSequence> smartReplies) {
352         mRankingBuilder.setSmartReplies(smartReplies);
353         return this;
354     }
355 
setSmartReplies(CharSequence... smartReplies)356     public NotificationEntryBuilder setSmartReplies(CharSequence... smartReplies) {
357         mRankingBuilder.setSmartReplies(smartReplies);
358         return this;
359     }
360 
setShortcutInfo(ShortcutInfo shortcutInfo)361     public NotificationEntryBuilder setShortcutInfo(ShortcutInfo shortcutInfo) {
362         mRankingBuilder.setShortcutInfo(shortcutInfo);
363         return this;
364     }
365 
setRankingAdjustment(int rankingAdjustment)366     public NotificationEntryBuilder setRankingAdjustment(int rankingAdjustment) {
367         mRankingBuilder.setRankingAdjustment(rankingAdjustment);
368         return this;
369     }
370 }
371