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.app.Notification; 23 import android.os.Build; 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 public final class Adjustment implements Parcelable { 46 private final String mPackage; 47 private final String mKey; 48 private final CharSequence mExplanation; 49 private final Bundle mSignals; 50 private final int mUser; 51 @Nullable private String mIssuer; 52 53 /** @hide */ 54 @StringDef (prefix = { "KEY_" }, value = { 55 KEY_PEOPLE, 56 KEY_SNOOZE_CRITERIA, 57 KEY_GROUP_KEY, 58 KEY_USER_SENTIMENT, 59 KEY_CONTEXTUAL_ACTIONS, 60 KEY_TEXT_REPLIES, 61 KEY_IMPORTANCE, 62 KEY_IMPORTANCE_PROPOSAL, 63 KEY_SENSITIVE_CONTENT, 64 KEY_RANKING_SCORE, 65 KEY_NOT_CONVERSATION 66 }) 67 @Retention(RetentionPolicy.SOURCE) 68 public @interface Keys {} 69 70 /** 71 * Data type: ArrayList of {@code String}, where each is a representation of a 72 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}. 73 * See {@link android.app.Notification.Builder#addPerson(String)}. 74 * @hide 75 */ 76 @SystemApi 77 public static final String KEY_PEOPLE = "key_people"; 78 79 /** 80 * Parcelable {@code ArrayList} of {@link SnoozeCriterion}. These criteria may be visible to 81 * users. If a user chooses to snooze a notification until one of these criterion, the 82 * assistant will be notified via 83 * {@link NotificationAssistantService#onNotificationSnoozedUntilContext}. 84 */ 85 public static final String KEY_SNOOZE_CRITERIA = "key_snooze_criteria"; 86 87 /** 88 * Data type: String. Used to change what {@link Notification#getGroup() group} a notification 89 * belongs to. 90 * @hide 91 */ 92 public static final String KEY_GROUP_KEY = "key_group_key"; 93 94 /** 95 * Data type: int, one of {@link NotificationListenerService.Ranking#USER_SENTIMENT_POSITIVE}, 96 * {@link NotificationListenerService.Ranking#USER_SENTIMENT_NEUTRAL}, 97 * {@link NotificationListenerService.Ranking#USER_SENTIMENT_NEGATIVE}. Used to express how 98 * a user feels about notifications in the same {@link android.app.NotificationChannel} as 99 * the notification represented by {@link #getKey()}. 100 */ 101 public static final String KEY_USER_SENTIMENT = "key_user_sentiment"; 102 103 /** 104 * Data type: ArrayList of {@link android.app.Notification.Action}. 105 * Used to suggest contextual actions for a notification. 106 * 107 * @see Notification.Action.Builder#setContextual(boolean) 108 */ 109 public static final String KEY_CONTEXTUAL_ACTIONS = "key_contextual_actions"; 110 111 /** 112 * Data type: ArrayList of {@link CharSequence}. 113 * Used to suggest smart replies for a notification. 114 */ 115 public static final String KEY_TEXT_REPLIES = "key_text_replies"; 116 117 /** 118 * Data type: int, one of importance values e.g. 119 * {@link android.app.NotificationManager#IMPORTANCE_MIN}. 120 * 121 * <p> If used from 122 * {@link NotificationAssistantService#onNotificationEnqueued(StatusBarNotification)}, and 123 * received before the notification is posted, it can block a notification from appearing or 124 * silence it. Importance adjustments received too late from 125 * {@link NotificationAssistantService#onNotificationEnqueued(StatusBarNotification)} will be 126 * ignored. 127 * </p> 128 * <p>If used from 129 * {@link NotificationAssistantService#adjustNotification(Adjustment)}, it can 130 * visually demote or cancel a notification, but use this with care if they notification was 131 * recently posted because the notification may already have made noise. 132 * </p> 133 */ 134 public static final String KEY_IMPORTANCE = "key_importance"; 135 136 /** 137 * Weaker than {@link #KEY_IMPORTANCE}, this adjustment suggests an importance rather than 138 * mandates an importance change. 139 * 140 * A notification listener can interpet this suggestion to show the user a prompt to change 141 * notification importance for the notification (or type, or app) moving forward. 142 * 143 * Data type: int, one of importance values e.g. 144 * {@link android.app.NotificationManager#IMPORTANCE_MIN}. 145 */ 146 public static final String KEY_IMPORTANCE_PROPOSAL = "key_importance_proposal"; 147 148 /** 149 * Data type: boolean, when true it suggests that the content text of this notification is 150 * sensitive. The system uses this information to improve privacy around the notification 151 * content. In {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, sensitive notification content is 152 * redacted from updates to most {@link NotificationListenerService 153 * NotificationListenerServices}. Also if an app posts a sensitive notification while 154 * {@link android.media.projection.MediaProjection screen-sharing} is active, that app's windows 155 * are blocked from screen-sharing and a {@link android.widget.Toast Toast} is shown to inform 156 * the user about this. 157 */ 158 public static final String KEY_SENSITIVE_CONTENT = "key_sensitive_content"; 159 160 /** 161 * Data type: float, a ranking score from 0 (lowest) to 1 (highest). 162 * Used to rank notifications inside that fall under the same classification (i.e. alerting, 163 * silenced). 164 */ 165 public static final String KEY_RANKING_SCORE = "key_ranking_score"; 166 167 /** 168 * Data type: boolean, when true it suggests this is NOT a conversation notification. 169 * @hide 170 */ 171 @SystemApi 172 public static final String KEY_NOT_CONVERSATION = "key_not_conversation"; 173 174 /** 175 * Create a notification adjustment. 176 * 177 * @param pkg The package of the notification. 178 * @param key The notification key. 179 * @param signals A bundle of signals that should inform notification display, ordering, and 180 * interruptiveness. 181 * @param explanation A human-readable justification for the adjustment. 182 * @hide 183 */ 184 @SystemApi Adjustment(String pkg, String key, Bundle signals, CharSequence explanation, int user)185 public Adjustment(String pkg, String key, Bundle signals, CharSequence explanation, int user) { 186 mPackage = pkg; 187 mKey = key; 188 mSignals = signals; 189 mExplanation = explanation; 190 mUser = user; 191 } 192 193 /** 194 * Create a notification adjustment. 195 * 196 * @param pkg The package of the notification. 197 * @param key The notification key. 198 * @param signals A bundle of signals that should inform notification display, ordering, and 199 * interruptiveness. 200 * @param explanation A human-readable justification for the adjustment. 201 * @param userHandle User handle for for whose the adjustments will be applied. 202 */ Adjustment(@onNull String pkg, @NonNull String key, @NonNull Bundle signals, @NonNull CharSequence explanation, @NonNull UserHandle userHandle)203 public Adjustment(@NonNull String pkg, @NonNull String key, @NonNull Bundle signals, 204 @NonNull CharSequence explanation, 205 @NonNull UserHandle userHandle) { 206 mPackage = pkg; 207 mKey = key; 208 mSignals = signals; 209 mExplanation = explanation; 210 mUser = userHandle.getIdentifier(); 211 } 212 213 /** 214 * @hide 215 */ 216 @SystemApi Adjustment(Parcel in)217 protected Adjustment(Parcel in) { 218 if (in.readInt() == 1) { 219 mPackage = in.readString(); 220 } else { 221 mPackage = null; 222 } 223 if (in.readInt() == 1) { 224 mKey = in.readString(); 225 } else { 226 mKey = null; 227 } 228 if (in.readInt() == 1) { 229 mExplanation = in.readCharSequence(); 230 } else { 231 mExplanation = null; 232 } 233 mSignals = in.readBundle(); 234 mUser = in.readInt(); 235 mIssuer = in.readString(); 236 } 237 238 public static final @android.annotation.NonNull Creator<Adjustment> CREATOR = new Creator<Adjustment>() { 239 @Override 240 public Adjustment createFromParcel(Parcel in) { 241 return new Adjustment(in); 242 } 243 244 @Override 245 public Adjustment[] newArray(int size) { 246 return new Adjustment[size]; 247 } 248 }; 249 getPackage()250 public @NonNull String getPackage() { 251 return mPackage; 252 } 253 getKey()254 public @NonNull String getKey() { 255 return mKey; 256 } 257 getExplanation()258 public @NonNull CharSequence getExplanation() { 259 return mExplanation; 260 } 261 getSignals()262 public @NonNull Bundle getSignals() { 263 return mSignals; 264 } 265 266 /** @hide */ 267 @SystemApi getUser()268 public int getUser() { 269 return mUser; 270 } 271 getUserHandle()272 public @NonNull UserHandle getUserHandle() { 273 return UserHandle.of(mUser); 274 } 275 276 @Override describeContents()277 public int describeContents() { 278 return 0; 279 } 280 281 @Override writeToParcel(Parcel dest, int flags)282 public void writeToParcel(Parcel dest, int flags) { 283 if (mPackage != null) { 284 dest.writeInt(1); 285 dest.writeString(mPackage); 286 } else { 287 dest.writeInt(0); 288 } 289 if (mKey != null) { 290 dest.writeInt(1); 291 dest.writeString(mKey); 292 } else { 293 dest.writeInt(0); 294 } 295 if (mExplanation != null) { 296 dest.writeInt(1); 297 dest.writeCharSequence(mExplanation); 298 } else { 299 dest.writeInt(0); 300 } 301 dest.writeBundle(mSignals); 302 dest.writeInt(mUser); 303 dest.writeString(mIssuer); 304 } 305 306 @NonNull 307 @Override toString()308 public String toString() { 309 return "Adjustment{" 310 + "mSignals=" + mSignals 311 + '}'; 312 } 313 314 /** @hide */ setIssuer(@ullable String issuer)315 public void setIssuer(@Nullable String issuer) { 316 mIssuer = issuer; 317 } 318 319 /** @hide */ getIssuer()320 public @Nullable String getIssuer() { 321 return mIssuer; 322 } 323 } 324