NotificationAssistantService.java revision b8f53ee812b75b526c3b481b62334e45609fa70e
1/* 2 * Copyright (C) 2015 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 17package android.service.notification; 18 19import android.annotation.SdkConstant; 20import android.annotation.SystemApi; 21import android.content.ComponentName; 22import android.content.Context; 23import android.content.Intent; 24import android.net.Uri; 25import android.os.Handler; 26import android.os.IBinder; 27import android.os.Looper; 28import android.os.Message; 29import android.os.RemoteException; 30import android.util.Log; 31import com.android.internal.os.SomeArgs; 32 33/** 34 * A service that helps the user manage notifications by modifying the 35 * relative importance of notifications. 36 * <p>To extend this class, you must declare the service in your manifest file with 37 * the {@link android.Manifest.permission#BIND_NOTIFICATION_ASSISTANT_SERVICE} permission 38 * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p> 39 * <pre> 40 * <service android:name=".NotificationAssistant" 41 * android:label="@string/service_name" 42 * android:permission="android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE"> 43 * <intent-filter> 44 * <action android:name="android.service.notification.NotificationAssistantService" /> 45 * </intent-filter> 46 * </service></pre> 47 * @hide 48 */ 49@SystemApi 50public abstract class NotificationAssistantService extends NotificationListenerService { 51 private static final String TAG = "NotificationAssistant"; 52 53 /** 54 * The {@link Intent} that must be declared as handled by the service. 55 */ 56 @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) 57 public static final String SERVICE_INTERFACE 58 = "android.service.notification.NotificationAssistantService"; 59 60 /** Notification was canceled by the status bar reporting a click. */ 61 public static final int REASON_DELEGATE_CLICK = 1; 62 63 /** Notification was canceled by the status bar reporting a user dismissal. */ 64 public static final int REASON_DELEGATE_CANCEL = 2; 65 66 /** Notification was canceled by the status bar reporting a user dismiss all. */ 67 public static final int REASON_DELEGATE_CANCEL_ALL = 3; 68 69 /** Notification was canceled by the status bar reporting an inflation error. */ 70 public static final int REASON_DELEGATE_ERROR = 4; 71 72 /** Notification was canceled by the package manager modifying the package. */ 73 public static final int REASON_PACKAGE_CHANGED = 5; 74 75 /** Notification was canceled by the owning user context being stopped. */ 76 public static final int REASON_USER_STOPPED = 6; 77 78 /** Notification was canceled by the user banning the package. */ 79 public static final int REASON_PACKAGE_BANNED = 7; 80 81 /** Notification was canceled by the app canceling this specific notification. */ 82 public static final int REASON_APP_CANCEL = 8; 83 84 /** Notification was canceled by the app cancelling all its notifications. */ 85 public static final int REASON_APP_CANCEL_ALL = 9; 86 87 /** Notification was canceled by a listener reporting a user dismissal. */ 88 public static final int REASON_LISTENER_CANCEL = 10; 89 90 /** Notification was canceled by a listener reporting a user dismiss all. */ 91 public static final int REASON_LISTENER_CANCEL_ALL = 11; 92 93 /** Notification was canceled because it was a member of a canceled group. */ 94 public static final int REASON_GROUP_SUMMARY_CANCELED = 12; 95 96 /** Notification was canceled because it was an invisible member of a group. */ 97 public static final int REASON_GROUP_OPTIMIZATION = 13; 98 99 /** Notification was canceled by the device administrator suspending the package. */ 100 public static final int REASON_PACKAGE_SUSPENDED = 14; 101 102 /** Notification was canceled by the owning managed profile being turned off. */ 103 public static final int REASON_PROFILE_TURNED_OFF = 15; 104 105 public class Adjustment { 106 int mImportance; 107 CharSequence mExplanation; 108 Uri mReference; 109 110 /** 111 * Create a notification importance adjustment. 112 * 113 * @param importance The final importance of the notification. 114 * @param explanation A human-readable justification for the adjustment. 115 * @param reference A reference to an external object that augments the 116 * explanation, such as a 117 * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}, 118 * or null. 119 */ 120 public Adjustment(int importance, CharSequence explanation, Uri reference) { 121 mImportance = importance; 122 mExplanation = explanation; 123 mReference = reference; 124 } 125 } 126 127 private Handler mHandler; 128 129 /** @hide */ 130 @Override 131 public void registerAsSystemService(Context context, ComponentName componentName, 132 int currentUser) throws RemoteException { 133 super.registerAsSystemService(context, componentName, currentUser); 134 mHandler = new MyHandler(getContext().getMainLooper()); 135 } 136 137 @Override 138 protected void attachBaseContext(Context base) { 139 super.attachBaseContext(base); 140 mHandler = new MyHandler(getContext().getMainLooper()); 141 } 142 143 @Override 144 public final IBinder onBind(Intent intent) { 145 if (mWrapper == null) { 146 mWrapper = new NotificationAssistantWrapper(); 147 } 148 return mWrapper; 149 } 150 151 /** 152 * A notification was posted by an app. Called before alert. 153 * 154 * @param sbn the new notification 155 * @param importance the initial importance of the notification. 156 * @param user true if the initial importance reflects an explicit user preference. 157 * @return an adjustment or null to take no action, within 100ms. 158 */ 159 abstract public Adjustment onNotificationEnqueued(StatusBarNotification sbn, 160 int importance, boolean user); 161 162 /** 163 * The visibility of a notification has changed. 164 * 165 * @param key the notification key 166 * @param time milliseconds since midnight, January 1, 1970 UTC. 167 * @param visible true if the notification became visible, false if hidden. 168 */ 169 public void onNotificationVisibilityChanged(String key, long time, boolean visible) 170 { 171 // Do nothing, Override this to collect visibility statistics. 172 } 173 174 /** 175 * The user clicked on a notification. 176 * 177 * @param key the notification key 178 * @param time milliseconds since midnight, January 1, 1970 UTC. 179 */ 180 public void onNotificationClick(String key, long time) 181 { 182 // Do nothing, Override this to collect click statistics 183 } 184 185 /** 186 * The user clicked on a notification action. 187 * 188 * @param key the notification key 189 * @param time milliseconds since midnight, January 1, 1970 UTC. 190 * @param actionIndex the index of the action button that was pressed. 191 */ 192 public void onNotificationActionClick(String key, long time, int actionIndex) 193 { 194 // Do nothing, Override this to collect action button click statistics 195 } 196 197 /** 198 * A notification was removed. 199 200 * @param key the notification key 201 * @param time milliseconds since midnight, January 1, 1970 UTC. 202 * @param reason see {@link #REASON_LISTENER_CANCEL}, etc. 203 */ 204 public void onNotificationRemoved(String key, long time, int reason) { 205 // Do nothing, Override this to collect dismissal statistics 206 } 207 208 /** 209 * Change the importance of an existing notification. N.B. this won’t cause 210 * an existing notification to alert, but might allow a future update to 211 * this notification to alert. 212 * 213 * @param key the notification key 214 * @param adjustment the new importance with an explanation 215 */ 216 public final void adjustImportance(String key, Adjustment adjustment) { 217 if (!isBound()) return; 218 try { 219 getNotificationInterface().setImportanceFromAssistant(mWrapper, key, 220 adjustment.mImportance, adjustment.mExplanation); 221 } catch (android.os.RemoteException ex) { 222 Log.v(TAG, "Unable to contact notification manager", ex); 223 } 224 } 225 226 private class NotificationAssistantWrapper extends NotificationListenerWrapper { 227 @Override 228 public void onNotificationEnqueued(IStatusBarNotificationHolder sbnHolder, 229 int importance, boolean user) { 230 StatusBarNotification sbn; 231 try { 232 sbn = sbnHolder.get(); 233 } catch (RemoteException e) { 234 Log.w(TAG, "onNotificationEnqueued: Error receiving StatusBarNotification", e); 235 return; 236 } 237 238 SomeArgs args = SomeArgs.obtain(); 239 args.arg1 = sbn; 240 args.argi1 = importance; 241 args.argi2 = user ? 1 : 0; 242 mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_ENQUEUED, 243 args).sendToTarget(); 244 } 245 246 @Override 247 public void onNotificationVisibilityChanged(String key, long time, boolean visible) { 248 SomeArgs args = SomeArgs.obtain(); 249 args.arg1 = key; 250 args.arg2 = time; 251 args.argi1 = visible ? 1 : 0; 252 mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_VISIBILITY_CHANGED, 253 args).sendToTarget(); 254 } 255 256 @Override 257 public void onNotificationClick(String key, long time) { 258 SomeArgs args = SomeArgs.obtain(); 259 args.arg1 = key; 260 args.arg2 = time; 261 mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_CLICK, 262 args).sendToTarget(); 263 } 264 265 @Override 266 public void onNotificationActionClick(String key, long time, int actionIndex) { 267 SomeArgs args = SomeArgs.obtain(); 268 args.arg1 = key; 269 args.arg2 = time; 270 args.argi1 = actionIndex; 271 mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_ACTION_CLICK, 272 args).sendToTarget(); 273 } 274 275 @Override 276 public void onNotificationRemovedReason(String key, long time, int reason) { 277 SomeArgs args = SomeArgs.obtain(); 278 args.arg1 = key; 279 args.arg2 = time; 280 args.argi1 = reason; 281 mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_REMOVED_REASON, 282 args).sendToTarget(); 283 } 284 } 285 286 private final class MyHandler extends Handler { 287 public static final int MSG_ON_NOTIFICATION_ENQUEUED = 1; 288 public static final int MSG_ON_NOTIFICATION_VISIBILITY_CHANGED = 2; 289 public static final int MSG_ON_NOTIFICATION_CLICK = 3; 290 public static final int MSG_ON_NOTIFICATION_ACTION_CLICK = 4; 291 public static final int MSG_ON_NOTIFICATION_REMOVED_REASON = 5; 292 293 public MyHandler(Looper looper) { 294 super(looper, null, false); 295 } 296 297 @Override 298 public void handleMessage(Message msg) { 299 switch (msg.what) { 300 case MSG_ON_NOTIFICATION_ENQUEUED: { 301 SomeArgs args = (SomeArgs) msg.obj; 302 StatusBarNotification sbn = (StatusBarNotification) args.arg1; 303 final int importance = args.argi1; 304 final boolean user = args.argi2 == 1; 305 args.recycle(); 306 Adjustment adjustment = onNotificationEnqueued(sbn, importance, user); 307 if (adjustment != null) { 308 adjustImportance(sbn.getKey(), adjustment); 309 } 310 } break; 311 312 case MSG_ON_NOTIFICATION_VISIBILITY_CHANGED: { 313 SomeArgs args = (SomeArgs) msg.obj; 314 final String key = (String) args.arg1; 315 final long time = (long) args.arg2; 316 final boolean visible = args.argi1 == 1; 317 args.recycle(); 318 onNotificationVisibilityChanged(key, time, visible); 319 } break; 320 321 case MSG_ON_NOTIFICATION_CLICK: { 322 SomeArgs args = (SomeArgs) msg.obj; 323 final String key = (String) args.arg1; 324 final long time = (long) args.arg2; 325 args.recycle(); 326 onNotificationClick(key, time); 327 } break; 328 329 case MSG_ON_NOTIFICATION_ACTION_CLICK: { 330 SomeArgs args = (SomeArgs) msg.obj; 331 final String key = (String) args.arg1; 332 final long time = (long) args.arg2; 333 final int actionIndex = args.argi1; 334 args.recycle(); 335 onNotificationActionClick(key, time, actionIndex); 336 } break; 337 338 case MSG_ON_NOTIFICATION_REMOVED_REASON: { 339 SomeArgs args = (SomeArgs) msg.obj; 340 final String key = (String) args.arg1; 341 final long time = (long) args.arg2; 342 final int reason = args.argi1; 343 args.recycle(); 344 onNotificationRemoved(key, time, reason); 345 } break; 346 } 347 } 348 } 349} 350