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