• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
16 package android.service.notification;
17 
18 import android.annotation.FlaggedApi;
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.StringDef;
23 import android.annotation.SystemApi;
24 import android.annotation.TestApi;
25 import android.app.Notification;
26 import android.os.Build;
27 import android.os.Bundle;
28 import android.os.Parcel;
29 import android.os.Parcelable;
30 import android.os.UserHandle;
31 
32 import java.lang.annotation.Retention;
33 import java.lang.annotation.RetentionPolicy;
34 
35 /**
36  * Ranking updates from the Assistant.
37  *
38  * The updates are provides as a {@link Bundle} of signals, using the keys provided in this
39  * class.
40  * Each {@code KEY} specifies what type of data it supports and what kind of Adjustment it
41  * realizes on the notification rankings.
42  *
43  * Notifications affected by the Adjustment will be re-ranked if necessary.
44  *
45  * @hide
46  */
47 @SystemApi
48 public final class Adjustment implements Parcelable {
49     private final String mPackage;
50     private final String mKey;
51     private final CharSequence mExplanation;
52     private final Bundle mSignals;
53     private final int mUser;
54     @Nullable private String mIssuer;
55 
56     /** @hide */
57     @StringDef (prefix = { "KEY_" }, value = {
58             KEY_PEOPLE,
59             KEY_SNOOZE_CRITERIA,
60             KEY_GROUP_KEY,
61             KEY_USER_SENTIMENT,
62             KEY_CONTEXTUAL_ACTIONS,
63             KEY_TEXT_REPLIES,
64             KEY_IMPORTANCE,
65             KEY_IMPORTANCE_PROPOSAL,
66             KEY_SENSITIVE_CONTENT,
67             KEY_RANKING_SCORE,
68             KEY_NOT_CONVERSATION,
69             KEY_TYPE,
70             KEY_SUMMARIZATION
71     })
72     @Retention(RetentionPolicy.SOURCE)
73     public @interface Keys {}
74 
75     /**
76      * Data type: ArrayList of {@code String}, where each is a representation of a
77      * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
78      * See {@link android.app.Notification.Builder#addPerson(String)}.
79      * @hide
80      */
81     @SystemApi
82     public static final String KEY_PEOPLE = "key_people";
83 
84     /**
85      * Parcelable {@code ArrayList} of {@link SnoozeCriterion}. These criteria may be visible to
86      * users. If a user chooses to snooze a notification until one of these criterion, the
87      * assistant will be notified via
88      * {@link NotificationAssistantService#onNotificationSnoozedUntilContext}.
89      */
90     public static final String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
91 
92     /**
93      * Data type: String. Used to change what {@link Notification#getGroup() group} a notification
94      * belongs to.
95      * @hide
96      */
97     public static final String KEY_GROUP_KEY = "key_group_key";
98 
99     /**
100      * Data type: int, one of {@link NotificationListenerService.Ranking#USER_SENTIMENT_POSITIVE},
101      * {@link NotificationListenerService.Ranking#USER_SENTIMENT_NEUTRAL},
102      * {@link NotificationListenerService.Ranking#USER_SENTIMENT_NEGATIVE}. Used to express how
103      * a user feels about notifications in the same {@link android.app.NotificationChannel} as
104      * the notification represented by {@link #getKey()}.
105      */
106     public static final String KEY_USER_SENTIMENT = "key_user_sentiment";
107 
108     /**
109      * Data type: ArrayList of {@link android.app.Notification.Action}.
110      * Used to suggest contextual actions for a notification.
111      *
112      * @see Notification.Action.Builder#setContextual(boolean)
113      */
114     public static final String KEY_CONTEXTUAL_ACTIONS = "key_contextual_actions";
115 
116     /**
117      * Data type: ArrayList of {@link CharSequence}.
118      * Used to suggest smart replies for a notification.
119      */
120     public static final String KEY_TEXT_REPLIES = "key_text_replies";
121 
122     /**
123      * Data type: int, one of importance values e.g.
124      * {@link android.app.NotificationManager#IMPORTANCE_MIN}.
125      *
126      * <p> If used from
127      * {@link NotificationAssistantService#onNotificationEnqueued(StatusBarNotification)}, and
128      * received before the notification is posted, it can block a notification from appearing or
129      * silence it. Importance adjustments received too late from
130      * {@link NotificationAssistantService#onNotificationEnqueued(StatusBarNotification)} will be
131      * ignored.
132      * </p>
133      * <p>If used from
134      * {@link NotificationAssistantService#adjustNotification(Adjustment)}, it can
135      * visually demote or cancel a notification, but use this with care if they notification was
136      * recently posted because the notification may already have made noise.
137      * </p>
138      */
139     public static final String KEY_IMPORTANCE = "key_importance";
140 
141     /**
142      * Weaker than {@link #KEY_IMPORTANCE}, this adjustment suggests an importance rather than
143      * mandates an importance change.
144      *
145      * A notification listener can interpet this suggestion to show the user a prompt to change
146      * notification importance for the notification (or type, or app) moving forward.
147      *
148      * Data type: int, one of importance values e.g.
149      * {@link android.app.NotificationManager#IMPORTANCE_MIN}.
150      */
151     public static final String KEY_IMPORTANCE_PROPOSAL = "key_importance_proposal";
152 
153     /**
154      * Data type: boolean, when true it suggests that the content text of this notification is
155      * sensitive. The system uses this information to improve privacy around the notification
156      * content. In {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, sensitive notification content is
157      * redacted from updates to most {@link NotificationListenerService
158      * NotificationListenerServices}. Also if an app posts a sensitive notification while
159      * {@link android.media.projection.MediaProjection screen-sharing} is active, that app's windows
160      * are blocked from screen-sharing and a {@link android.widget.Toast Toast} is shown to inform
161      * the user about this.
162      */
163     public static final String KEY_SENSITIVE_CONTENT = "key_sensitive_content";
164 
165     /**
166      * Data type: float, a ranking score from 0 (lowest) to 1 (highest).
167      * Used to rank notifications inside that fall under the same classification (i.e. alerting,
168      * silenced).
169      */
170     public static final String KEY_RANKING_SCORE = "key_ranking_score";
171 
172     /**
173      * Data type: boolean, when true it suggests this is NOT a conversation notification.
174      * @hide
175      */
176     @SystemApi
177     public static final String KEY_NOT_CONVERSATION = "key_not_conversation";
178 
179     /**
180      * Data type: int, the classification type of this notification. The OS may display
181      * notifications differently depending on the type, and may change the alerting level of the
182      * notification.
183      */
184     @FlaggedApi(Flags.FLAG_NOTIFICATION_CLASSIFICATION)
185     public static final String KEY_TYPE = "key_type";
186 
187     /** @hide */
188     @IntDef(prefix = { "TYPE_" }, value = {
189             TYPE_OTHER,
190             TYPE_PROMOTION,
191             TYPE_SOCIAL_MEDIA,
192             TYPE_NEWS,
193             TYPE_CONTENT_RECOMMENDATION,
194     })
195     @Retention(RetentionPolicy.SOURCE)
196     public @interface Types {}
197 
198     /**
199      * This notification can be categorized, but not into one of the other categories known to the
200      * OS at a given version.
201      */
202     @FlaggedApi(Flags.FLAG_NOTIFICATION_CLASSIFICATION)
203     public static final int TYPE_OTHER = 0;
204     /**
205      * The type of this notification is a promotion/deal.
206      */
207     @FlaggedApi(Flags.FLAG_NOTIFICATION_CLASSIFICATION)
208     public static final int TYPE_PROMOTION = 1;
209     /**
210      * The type of this notification is social media content that isn't a
211      * {@link Notification.Builder#setShortcutId(String) conversation}.
212      */
213     @FlaggedApi(Flags.FLAG_NOTIFICATION_CLASSIFICATION)
214     public static final int TYPE_SOCIAL_MEDIA = 2;
215     /**
216      * The type of this notification is news.
217      */
218     @FlaggedApi(Flags.FLAG_NOTIFICATION_CLASSIFICATION)
219     public static final int TYPE_NEWS = 3;
220     /**
221      * The type of this notification is content recommendation, for example new videos or books the
222      * user may be interested in.
223      */
224     @FlaggedApi(Flags.FLAG_NOTIFICATION_CLASSIFICATION)
225     public static final int TYPE_CONTENT_RECOMMENDATION = 4;
226 
227     /**
228      * Data type: CharSequence, a summarization of the text of the notification, or, if provided for
229      * a group summary, a summarization of the text of all of the notificatrions in the group.
230      * Send this key with a null value to remove an existing summarization.
231      */
232     @FlaggedApi(android.app.Flags.FLAG_NM_SUMMARIZATION)
233     public static final String KEY_SUMMARIZATION = "key_summarization";
234 
235     /**
236      * Create a notification adjustment.
237      *
238      * @param pkg The package of the notification.
239      * @param key The notification key.
240      * @param signals A bundle of signals that should inform notification display, ordering, and
241      *                interruptiveness.
242      * @param explanation A human-readable justification for the adjustment.
243      * @hide
244      */
245     @SystemApi
Adjustment(String pkg, String key, Bundle signals, CharSequence explanation, int user)246     public Adjustment(String pkg, String key, Bundle signals, CharSequence explanation, int user) {
247         mPackage = pkg;
248         mKey = key;
249         mSignals = signals;
250         mExplanation = explanation;
251         mUser = user;
252     }
253 
254     /**
255      * Create a notification adjustment.
256      *
257      * @param pkg The package of the notification.
258      * @param key The notification key.
259      * @param signals A bundle of signals that should inform notification display, ordering, and
260      *                interruptiveness.
261      * @param explanation A human-readable justification for the adjustment.
262      * @param userHandle User handle for for whose the adjustments will be applied.
263      */
Adjustment(@onNull String pkg, @NonNull String key, @NonNull Bundle signals, @NonNull CharSequence explanation, @NonNull UserHandle userHandle)264     public Adjustment(@NonNull String pkg, @NonNull String key, @NonNull Bundle signals,
265             @NonNull CharSequence explanation,
266             @NonNull UserHandle userHandle) {
267         mPackage = pkg;
268         mKey = key;
269         mSignals = signals;
270         mExplanation = explanation;
271         mUser = userHandle.getIdentifier();
272     }
273 
274     /**
275      * @hide
276      */
277     @SystemApi
Adjustment(Parcel in)278     protected Adjustment(Parcel in) {
279         if (in.readInt() == 1) {
280             mPackage = in.readString();
281         } else {
282             mPackage = null;
283         }
284         if (in.readInt() == 1) {
285             mKey = in.readString();
286         } else {
287             mKey = null;
288         }
289         if (in.readInt() == 1) {
290             mExplanation = in.readCharSequence();
291         } else {
292             mExplanation = null;
293         }
294         mSignals = in.readBundle();
295         mUser = in.readInt();
296         mIssuer = in.readString();
297     }
298 
299     public static final @android.annotation.NonNull Creator<Adjustment> CREATOR = new Creator<Adjustment>() {
300         @Override
301         public Adjustment createFromParcel(Parcel in) {
302             return new Adjustment(in);
303         }
304 
305         @Override
306         public Adjustment[] newArray(int size) {
307             return new Adjustment[size];
308         }
309     };
310 
getPackage()311     public @NonNull String getPackage() {
312         return mPackage;
313     }
314 
getKey()315     public @NonNull String getKey() {
316         return mKey;
317     }
318 
getExplanation()319     public @NonNull CharSequence getExplanation() {
320         return mExplanation;
321     }
322 
getSignals()323     public @NonNull Bundle getSignals() {
324         return mSignals;
325     }
326 
327     /** @hide */
328     @SystemApi
getUser()329     public int getUser() {
330         return mUser;
331     }
332 
getUserHandle()333     public @NonNull UserHandle getUserHandle() {
334         return UserHandle.of(mUser);
335     }
336 
337     @Override
describeContents()338     public int describeContents() {
339         return 0;
340     }
341 
342     @Override
writeToParcel(Parcel dest, int flags)343     public void writeToParcel(Parcel dest, int flags) {
344         if (mPackage != null) {
345             dest.writeInt(1);
346             dest.writeString(mPackage);
347         } else {
348             dest.writeInt(0);
349         }
350         if (mKey != null) {
351             dest.writeInt(1);
352             dest.writeString(mKey);
353         } else {
354             dest.writeInt(0);
355         }
356         if (mExplanation != null) {
357             dest.writeInt(1);
358             dest.writeCharSequence(mExplanation);
359         } else {
360             dest.writeInt(0);
361         }
362         dest.writeBundle(mSignals);
363         dest.writeInt(mUser);
364         dest.writeString(mIssuer);
365     }
366 
367     @NonNull
368     @Override
toString()369     public String toString() {
370         return "Adjustment{"
371                 + "mSignals=" + mSignals
372                 + '}';
373     }
374 
375     /** @hide */
setIssuer(@ullable String issuer)376     public void setIssuer(@Nullable String issuer) {
377         mIssuer = issuer;
378     }
379 
380     /** @hide */
getIssuer()381     public @Nullable String getIssuer() {
382         return mIssuer;
383     }
384 }
385