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.NonNull;
19 import android.annotation.Nullable;
20 import android.annotation.StringDef;
21 import android.annotation.SystemApi;
22 import android.annotation.TestApi;
23 import android.app.Notification;
24 import android.os.Bundle;
25 import android.os.Parcel;
26 import android.os.Parcelable;
27 import android.os.UserHandle;
28 
29 import java.lang.annotation.Retention;
30 import java.lang.annotation.RetentionPolicy;
31 
32 /**
33  * Ranking updates from the Assistant.
34  *
35  * The updates are provides as a {@link Bundle} of signals, using the keys provided in this
36  * class.
37  * Each {@code KEY} specifies what type of data it supports and what kind of Adjustment it
38  * realizes on the notification rankings.
39  *
40  * Notifications affected by the Adjustment will be re-ranked if necessary.
41  *
42  * @hide
43  */
44 @SystemApi
45 @TestApi
46 public final class Adjustment implements Parcelable {
47     private final String mPackage;
48     private final String mKey;
49     private final CharSequence mExplanation;
50     private final Bundle mSignals;
51     private final int mUser;
52     @Nullable private String mIssuer;
53 
54     /** @hide */
55     @StringDef (prefix = { "KEY_" }, value = {
56             KEY_CONTEXTUAL_ACTIONS, KEY_GROUP_KEY, KEY_IMPORTANCE, KEY_PEOPLE, KEY_SNOOZE_CRITERIA,
57             KEY_TEXT_REPLIES, KEY_USER_SENTIMENT
58     })
59     @Retention(RetentionPolicy.SOURCE)
60     public @interface Keys {}
61 
62     /**
63      * Data type: ArrayList of {@code String}, where each is a representation of a
64      * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
65      * See {@link android.app.Notification.Builder#addPerson(String)}.
66      * @hide
67      */
68     @SystemApi
69     public static final String KEY_PEOPLE = "key_people";
70     /**
71      * Parcelable {@code ArrayList} of {@link SnoozeCriterion}. These criteria may be visible to
72      * users. If a user chooses to snooze a notification until one of these criterion, the
73      * assistant will be notified via
74      * {@link NotificationAssistantService#onNotificationSnoozedUntilContext}.
75      */
76     public static final String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
77     /**
78      * Data type: String. Used to change what {@link Notification#getGroup() group} a notification
79      * belongs to.
80      * @hide
81      */
82     public static final String KEY_GROUP_KEY = "key_group_key";
83 
84     /**
85      * Data type: int, one of {@link NotificationListenerService.Ranking#USER_SENTIMENT_POSITIVE},
86      * {@link NotificationListenerService.Ranking#USER_SENTIMENT_NEUTRAL},
87      * {@link NotificationListenerService.Ranking#USER_SENTIMENT_NEGATIVE}. Used to express how
88      * a user feels about notifications in the same {@link android.app.NotificationChannel} as
89      * the notification represented by {@link #getKey()}.
90      */
91     public static final String KEY_USER_SENTIMENT = "key_user_sentiment";
92 
93     /**
94      * Data type: ArrayList of {@link android.app.Notification.Action}.
95      * Used to suggest contextual actions for a notification.
96      *
97      * @see Notification.Action.Builder#setContextual(boolean)
98      */
99     public static final String KEY_CONTEXTUAL_ACTIONS = "key_contextual_actions";
100 
101     /**
102      * Data type: ArrayList of {@link CharSequence}.
103      * Used to suggest smart replies for a notification.
104      */
105     public static final String KEY_TEXT_REPLIES = "key_text_replies";
106 
107     /**
108      * Data type: int, one of importance values e.g.
109      * {@link android.app.NotificationManager#IMPORTANCE_MIN}.
110      *
111      * <p> If used from
112      * {@link NotificationAssistantService#onNotificationEnqueued(StatusBarNotification)}, and
113      * received before the notification is posted, it can block a notification from appearing or
114      * silence it. Importance adjustments received too late from
115      * {@link NotificationAssistantService#onNotificationEnqueued(StatusBarNotification)} will be
116      * ignored.
117      * </p>
118      * <p>If used from
119      * {@link NotificationAssistantService#adjustNotification(Adjustment)}, it can
120      * visually demote or cancel a notification, but use this with care if they notification was
121      * recently posted because the notification may already have made noise.
122      * </p>
123      */
124     public static final String KEY_IMPORTANCE = "key_importance";
125 
126     /**
127      * Create a notification adjustment.
128      *
129      * @param pkg The package of the notification.
130      * @param key The notification key.
131      * @param signals A bundle of signals that should inform notification display, ordering, and
132      *                interruptiveness.
133      * @param explanation A human-readable justification for the adjustment.
134      * @hide
135      */
136     @SystemApi
137     @TestApi
Adjustment(String pkg, String key, Bundle signals, CharSequence explanation, int user)138     public Adjustment(String pkg, String key, Bundle signals, CharSequence explanation, int user) {
139         mPackage = pkg;
140         mKey = key;
141         mSignals = signals;
142         mExplanation = explanation;
143         mUser = user;
144     }
145 
146     /**
147      * Create a notification adjustment.
148      *
149      * @param pkg The package of the notification.
150      * @param key The notification key.
151      * @param signals A bundle of signals that should inform notification display, ordering, and
152      *                interruptiveness.
153      * @param explanation A human-readable justification for the adjustment.
154      * @param userHandle User handle for for whose the adjustments will be applied.
155      */
Adjustment(@onNull String pkg, @NonNull String key, @NonNull Bundle signals, @NonNull CharSequence explanation, @NonNull UserHandle userHandle)156     public Adjustment(@NonNull String pkg, @NonNull String key, @NonNull Bundle signals,
157             @NonNull CharSequence explanation,
158             @NonNull UserHandle userHandle) {
159         mPackage = pkg;
160         mKey = key;
161         mSignals = signals;
162         mExplanation = explanation;
163         mUser = userHandle.getIdentifier();
164     }
165 
166     /**
167      * @hide
168      */
169     @SystemApi
Adjustment(Parcel in)170     protected Adjustment(Parcel in) {
171         if (in.readInt() == 1) {
172             mPackage = in.readString();
173         } else {
174             mPackage = null;
175         }
176         if (in.readInt() == 1) {
177             mKey = in.readString();
178         } else {
179             mKey = null;
180         }
181         if (in.readInt() == 1) {
182             mExplanation = in.readCharSequence();
183         } else {
184             mExplanation = null;
185         }
186         mSignals = in.readBundle();
187         mUser = in.readInt();
188         mIssuer = in.readString();
189     }
190 
191     public static final @android.annotation.NonNull Creator<Adjustment> CREATOR = new Creator<Adjustment>() {
192         @Override
193         public Adjustment createFromParcel(Parcel in) {
194             return new Adjustment(in);
195         }
196 
197         @Override
198         public Adjustment[] newArray(int size) {
199             return new Adjustment[size];
200         }
201     };
202 
getPackage()203     public @NonNull String getPackage() {
204         return mPackage;
205     }
206 
getKey()207     public @NonNull String getKey() {
208         return mKey;
209     }
210 
getExplanation()211     public @NonNull CharSequence getExplanation() {
212         return mExplanation;
213     }
214 
getSignals()215     public @NonNull Bundle getSignals() {
216         return mSignals;
217     }
218 
219     /** @hide */
220     @SystemApi
221     @TestApi
getUser()222     public int getUser() {
223         return mUser;
224     }
225 
getUserHandle()226     public @NonNull UserHandle getUserHandle() {
227         return UserHandle.of(mUser);
228     }
229 
230     @Override
describeContents()231     public int describeContents() {
232         return 0;
233     }
234 
235     @Override
writeToParcel(Parcel dest, int flags)236     public void writeToParcel(Parcel dest, int flags) {
237         if (mPackage != null) {
238             dest.writeInt(1);
239             dest.writeString(mPackage);
240         } else {
241             dest.writeInt(0);
242         }
243         if (mKey != null) {
244             dest.writeInt(1);
245             dest.writeString(mKey);
246         } else {
247             dest.writeInt(0);
248         }
249         if (mExplanation != null) {
250             dest.writeInt(1);
251             dest.writeCharSequence(mExplanation);
252         } else {
253             dest.writeInt(0);
254         }
255         dest.writeBundle(mSignals);
256         dest.writeInt(mUser);
257         dest.writeString(mIssuer);
258     }
259 
260     @NonNull
261     @Override
toString()262     public String toString() {
263         return "Adjustment{"
264                 + "mSignals=" + mSignals
265                 + '}';
266     }
267 
268     /** @hide */
setIssuer(@ullable String issuer)269     public void setIssuer(@Nullable String issuer) {
270         mIssuer = issuer;
271     }
272 
273     /** @hide */
getIssuer()274     public @Nullable String getIssuer() {
275         return mIssuer;
276     }
277 }
278