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