NotificationListenerService.java revision a263e4e438746f91fb78857bd569ba4f796a346d
1/* 2 * Copyright (C) 2013 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.app.INotificationManager; 21import android.app.Service; 22import android.content.Context; 23import android.content.Intent; 24import android.os.IBinder; 25import android.os.ServiceManager; 26import android.os.UserHandle; 27import android.util.Log; 28 29/** 30 * A service that receives calls from the system when new notifications are posted or removed. 31 * <p>To extend this class, you must declare the service in your manifest file with 32 * the {@link android.Manifest.permission#BIND_NOTIFICATION_LISTENER_SERVICE} permission 33 * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p> 34 * <pre> 35 * <service android:name=".NotificationListener" 36 * android:label="@string/service_name" 37 * android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"> 38 * <intent-filter> 39 * <action android:name="android.service.notification.NotificationListenerService" /> 40 * </intent-filter> 41 * </service></pre> 42 */ 43public abstract class NotificationListenerService extends Service { 44 // TAG = "NotificationListenerService[MySubclass]" 45 private final String TAG = NotificationListenerService.class.getSimpleName() 46 + "[" + getClass().getSimpleName() + "]"; 47 48 private INotificationListenerWrapper mWrapper = null; 49 50 private INotificationManager mNoMan; 51 52 /** 53 * The {@link Intent} that must be declared as handled by the service. 54 */ 55 @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) 56 public static final String SERVICE_INTERFACE 57 = "android.service.notification.NotificationListenerService"; 58 59 /** 60 * Implement this method to learn about new notifications as they are posted by apps. 61 * 62 * @param sbn A data structure encapsulating the original {@link android.app.Notification} 63 * object as well as its identifying information (tag and id) and source 64 * (package name). 65 */ 66 public abstract void onNotificationPosted(StatusBarNotification sbn); 67 68 /** 69 * Implement this method to learn when notifications are removed. 70 * <P> 71 * This might occur because the user has dismissed the notification using system UI (or another 72 * notification listener) or because the app has withdrawn the notification. 73 * <P> 74 * NOTE: The {@link StatusBarNotification} object you receive will be "light"; that is, the 75 * result from {@link StatusBarNotification#getNotification} may be missing some heavyweight 76 * fields such as {@link android.app.Notification#contentView} and 77 * {@link android.app.Notification#largeIcon}. However, all other fields on 78 * {@link StatusBarNotification}, sufficient to match this call with a prior call to 79 * {@link #onNotificationPosted(StatusBarNotification)}, will be intact. 80 * 81 * @param sbn A data structure encapsulating at least the original information (tag and id) 82 * and source (package name) used to post the {@link android.app.Notification} that 83 * was just removed. 84 */ 85 public abstract void onNotificationRemoved(StatusBarNotification sbn); 86 87 /** 88 * Implement this method to learn about when the listener is enabled and connected to 89 * the notification manager. You are safe to call {@link #getActiveNotifications(String[]) 90 * at this time. 91 * 92 * @param notificationKeys The notification keys for all currently posted notifications. 93 */ 94 public void onListenerConnected(String[] notificationKeys) { 95 // optional 96 } 97 98 private final INotificationManager getNotificationInterface() { 99 if (mNoMan == null) { 100 mNoMan = INotificationManager.Stub.asInterface( 101 ServiceManager.getService(Context.NOTIFICATION_SERVICE)); 102 } 103 return mNoMan; 104 } 105 106 /** 107 * Inform the notification manager about dismissal of a single notification. 108 * <p> 109 * Use this if your listener has a user interface that allows the user to dismiss individual 110 * notifications, similar to the behavior of Android's status bar and notification panel. 111 * It should be called after the user dismisses a single notification using your UI; 112 * upon being informed, the notification manager will actually remove the notification 113 * and you will get an {@link #onNotificationRemoved(StatusBarNotification)} callback. 114 * <P> 115 * <b>Note:</b> If your listener allows the user to fire a notification's 116 * {@link android.app.Notification#contentIntent} by tapping/clicking/etc., you should call 117 * this method at that time <i>if</i> the Notification in question has the 118 * {@link android.app.Notification#FLAG_AUTO_CANCEL} flag set. 119 * 120 * @param pkg Package of the notifying app. 121 * @param tag Tag of the notification as specified by the notifying app in 122 * {@link android.app.NotificationManager#notify(String, int, android.app.Notification)}. 123 * @param id ID of the notification as specified by the notifying app in 124 * {@link android.app.NotificationManager#notify(String, int, android.app.Notification)}. 125 * <p> 126 * @deprecated Use {@link #cancelNotification(String key)} 127 * instead. Beginning with {@link android.os.Build.VERSION_CODES#L} this method will no longer 128 * cancel the notification. It will continue to cancel the notification for applications 129 * whose {@code targetSdkVersion} is earlier than {@link android.os.Build.VERSION_CODES#L}. 130 */ 131 public final void cancelNotification(String pkg, String tag, int id) { 132 if (!isBound()) return; 133 try { 134 getNotificationInterface().cancelNotificationFromListener( 135 mWrapper, pkg, tag, id); 136 } catch (android.os.RemoteException ex) { 137 Log.v(TAG, "Unable to contact notification manager", ex); 138 } 139 } 140 141 /** 142 * Inform the notification manager about dismissal of a single notification. 143 * <p> 144 * Use this if your listener has a user interface that allows the user to dismiss individual 145 * notifications, similar to the behavior of Android's status bar and notification panel. 146 * It should be called after the user dismisses a single notification using your UI; 147 * upon being informed, the notification manager will actually remove the notification 148 * and you will get an {@link #onNotificationRemoved(StatusBarNotification)} callback. 149 * <P> 150 * <b>Note:</b> If your listener allows the user to fire a notification's 151 * {@link android.app.Notification#contentIntent} by tapping/clicking/etc., you should call 152 * this method at that time <i>if</i> the Notification in question has the 153 * {@link android.app.Notification#FLAG_AUTO_CANCEL} flag set. 154 * <p> 155 * @param key Notification to dismiss from {@link StatusBarNotification#getKey()}. 156 */ 157 public final void cancelNotification(String key) { 158 if (!isBound()) return; 159 try { 160 getNotificationInterface().cancelNotificationsFromListener(mWrapper, 161 new String[] {key}); 162 } catch (android.os.RemoteException ex) { 163 Log.v(TAG, "Unable to contact notification manager", ex); 164 } 165 } 166 167 /** 168 * Inform the notification manager about dismissal of all notifications. 169 * <p> 170 * Use this if your listener has a user interface that allows the user to dismiss all 171 * notifications, similar to the behavior of Android's status bar and notification panel. 172 * It should be called after the user invokes the "dismiss all" function of your UI; 173 * upon being informed, the notification manager will actually remove all active notifications 174 * and you will get multiple {@link #onNotificationRemoved(StatusBarNotification)} callbacks. 175 * 176 * {@see #cancelNotification(String, String, int)} 177 */ 178 public final void cancelAllNotifications() { 179 cancelNotifications(null /*all*/); 180 } 181 182 /** 183 * Inform the notification manager about dismissal of specific notifications. 184 * <p> 185 * Use this if your listener has a user interface that allows the user to dismiss 186 * multiple notifications at once. 187 * 188 * @param keys Notifications to dismiss, or {@code null} to dismiss all. 189 * 190 * {@see #cancelNotification(String, String, int)} 191 */ 192 public final void cancelNotifications(String[] keys) { 193 if (!isBound()) return; 194 try { 195 getNotificationInterface().cancelNotificationsFromListener(mWrapper, keys); 196 } catch (android.os.RemoteException ex) { 197 Log.v(TAG, "Unable to contact notification manager", ex); 198 } 199 } 200 201 /** 202 * Request the list of outstanding notifications (that is, those that are visible to the 203 * current user). Useful when you don't know what's already been posted. 204 * 205 * @return An array of active notifications. 206 */ 207 public StatusBarNotification[] getActiveNotifications() { 208 return getActiveNotifications(null /*all*/); 209 } 210 211 /** 212 * Request the list of outstanding notifications (that is, those that are visible to the 213 * current user). Useful when you don't know what's already been posted. 214 * 215 * @param keys A specific list of notification keys, or {@code null} for all. 216 * @return An array of active notifications. 217 */ 218 public StatusBarNotification[] getActiveNotifications(String[] keys) { 219 if (!isBound()) return null; 220 try { 221 return getNotificationInterface().getActiveNotificationsFromListener(mWrapper, keys); 222 } catch (android.os.RemoteException ex) { 223 Log.v(TAG, "Unable to contact notification manager", ex); 224 } 225 return null; 226 } 227 228 /** 229 * Request the list of outstanding notification keys(that is, those that are visible to the 230 * current user). You can use the notification keys for subsequent retrieval via 231 * {@link #getActiveNotifications(String[]) or dismissal via 232 * {@link #cancelNotifications(String[]). 233 * 234 * @return An array of active notification keys. 235 */ 236 public String[] getActiveNotificationKeys() { 237 if (!isBound()) return null; 238 try { 239 return getNotificationInterface().getActiveNotificationKeysFromListener(mWrapper); 240 } catch (android.os.RemoteException ex) { 241 Log.v(TAG, "Unable to contact notification manager", ex); 242 } 243 return null; 244 } 245 246 @Override 247 public IBinder onBind(Intent intent) { 248 if (mWrapper == null) { 249 mWrapper = new INotificationListenerWrapper(); 250 } 251 return mWrapper; 252 } 253 254 private boolean isBound() { 255 if (mWrapper == null) { 256 Log.w(TAG, "Notification listener service not yet bound."); 257 return false; 258 } 259 return true; 260 } 261 262 private class INotificationListenerWrapper extends INotificationListener.Stub { 263 @Override 264 public void onNotificationPosted(StatusBarNotification sbn) { 265 try { 266 NotificationListenerService.this.onNotificationPosted(sbn); 267 } catch (Throwable t) { 268 Log.w(TAG, "Error running onNotificationPosted", t); 269 } 270 } 271 @Override 272 public void onNotificationRemoved(StatusBarNotification sbn) { 273 try { 274 NotificationListenerService.this.onNotificationRemoved(sbn); 275 } catch (Throwable t) { 276 Log.w(TAG, "Error running onNotificationRemoved", t); 277 } 278 } 279 @Override 280 public void onListenerConnected(String[] notificationKeys) { 281 try { 282 NotificationListenerService.this.onListenerConnected(notificationKeys); 283 } catch (Throwable t) { 284 Log.w(TAG, "Error running onListenerConnected", t); 285 } 286 } 287 } 288} 289