NotificationListenerService.java revision a4294297d46cc0b9f45897bc688c267502cce3ef
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 * &lt;service android:name=".NotificationListener"
35 *          android:label="&#64;string/service_name"
36 *          android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
37 *     &lt;intent-filter>
38 *         &lt;action android:name="android.service.notification.NotificationListenerService" />
39 *     &lt;/intent-filter>
40 * &lt;/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    /**
196     * Request the list of outstanding notification keys(that is, those that are visible to the
197     * current user).  You can use the notification keys for subsequent retrieval via
198     * {@link #getActiveNotifications(String[]) or dismissal via
199     * {@link #cancelNotifications(String[]).
200     *
201     * @return An array of active notification keys.
202     */
203    public String[] getActiveNotificationKeys() {
204        if (!isBound()) return null;
205        try {
206            return getNotificationInterface().getActiveNotificationKeysFromListener(mWrapper);
207        } catch (android.os.RemoteException ex) {
208            Log.v(TAG, "Unable to contact notification manager", ex);
209        }
210        return null;
211    }
212
213    @Override
214    public IBinder onBind(Intent intent) {
215        if (mWrapper == null) {
216            mWrapper = new INotificationListenerWrapper();
217        }
218        return mWrapper;
219    }
220
221    private boolean isBound() {
222        if (mWrapper == null) {
223            Log.w(TAG, "Notification listener service not yet bound.");
224            return false;
225        }
226        return true;
227    }
228
229    private class INotificationListenerWrapper extends INotificationListener.Stub {
230        @Override
231        public void onNotificationPosted(StatusBarNotification sbn) {
232            try {
233                NotificationListenerService.this.onNotificationPosted(sbn);
234            } catch (Throwable t) {
235                Log.w(TAG, "Error running onNotificationPosted", t);
236            }
237        }
238        @Override
239        public void onNotificationRemoved(StatusBarNotification sbn) {
240            try {
241                NotificationListenerService.this.onNotificationRemoved(sbn);
242            } catch (Throwable t) {
243                Log.w(TAG, "Error running onNotificationRemoved", t);
244            }
245        }
246        @Override
247        public void onListenerConnected(String[] notificationKeys) {
248            try {
249                NotificationListenerService.this.onListenerConnected(notificationKeys);
250            } catch (Throwable t) {
251                Log.w(TAG, "Error running onListenerConnected", t);
252            }
253        }
254    }
255}
256